flutter_smartconfig/android/src/main/java/com/espressif/iot/esptouch/task/__EsptouchTask.java

356 lines
14 KiB
Java

package com.espressif.iot.esptouch.task;
import android.content.Context;
import android.os.Looper;
import android.util.Log;
import com.espressif.iot.esptouch.EsptouchResult;
import com.espressif.iot.esptouch.IEsptouchListener;
import com.espressif.iot.esptouch.IEsptouchResult;
import com.espressif.iot.esptouch.IEsptouchTask;
import com.espressif.iot.esptouch.protocol.EsptouchGenerator;
import com.espressif.iot.esptouch.protocol.TouchData;
import com.espressif.iot.esptouch.udp.UDPSocketClient;
import com.espressif.iot.esptouch.udp.UDPSocketServer;
import com.espressif.iot.esptouch.util.ByteUtil;
import com.espressif.iot.esptouch.util.EspAES;
import com.espressif.iot.esptouch.util.EspNetUtil;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class __EsptouchTask implements __IEsptouchTask {
/**
* one indivisible data contain 3 9bits info
*/
private static final int ONE_DATA_LEN = 3;
private static final String TAG = "__EsptouchTask";
private final UDPSocketClient mSocketClient;
private final UDPSocketServer mSocketServer;
private final byte[] mApSsid;
private final byte[] mApPassword;
private final byte[] mApBssid;
private final boolean mIsSsidHidden;
private final Context mContext;
private volatile List<IEsptouchResult> mEsptouchResultList;
private volatile boolean mIsSuc = false;
private volatile boolean mIsInterrupt = false;
private volatile boolean mIsExecuted = false;
private AtomicBoolean mIsCancelled;
private IEsptouchTaskParameter mParameter;
private volatile Map<String, Integer> mBssidTaskSucCountMap;
private IEsptouchListener mEsptouchListener;
private Thread mTask;
public __EsptouchTask(Context context, TouchData apSsid, TouchData apBssid, TouchData apPassword, EspAES espAES,
IEsptouchTaskParameter parameter, boolean isSsidHidden) {
Log.i(TAG, "Welcome Esptouch " + IEsptouchTask.ESPTOUCH_VERSION);
mContext = context;
if (espAES == null) {
mApSsid = apSsid.getData();
mApPassword = apPassword.getData();
} else {
mApSsid = espAES.encrypt(apSsid.getData());
mApPassword = espAES.encrypt(apPassword.getData());
}
mApBssid = apBssid.getData();
mIsCancelled = new AtomicBoolean(false);
mSocketClient = new UDPSocketClient();
mParameter = parameter;
mSocketServer = new UDPSocketServer(mParameter.getPortListening(),
mParameter.getWaitUdpTotalMillisecond(), context);
mIsSsidHidden = isSsidHidden;
mEsptouchResultList = new ArrayList<>();
mBssidTaskSucCountMap = new HashMap<>();
}
private void __putEsptouchResult(boolean isSuc, String bssid, InetAddress inetAddress) {
synchronized (mEsptouchResultList) {
// check whether the result receive enough UDP response
boolean isTaskSucCountEnough = false;
Integer count = mBssidTaskSucCountMap.get(bssid);
if (count == null) {
count = 0;
}
++count;
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "__putEsptouchResult(): count = " + count);
}
mBssidTaskSucCountMap.put(bssid, count);
isTaskSucCountEnough = count >= mParameter
.getThresholdSucBroadcastCount();
if (!isTaskSucCountEnough) {
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "__putEsptouchResult(): count = " + count
+ ", isn't enough");
}
return;
}
// check whether the result is in the mEsptouchResultList already
boolean isExist = false;
for (IEsptouchResult esptouchResultInList : mEsptouchResultList) {
if (esptouchResultInList.getBssid().equals(bssid)) {
isExist = true;
break;
}
}
// only add the result who isn't in the mEsptouchResultList
if (!isExist) {
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "__putEsptouchResult(): put one more result");
}
final IEsptouchResult esptouchResult = new EsptouchResult(isSuc,
bssid, inetAddress);
mEsptouchResultList.add(esptouchResult);
if (mEsptouchListener != null) {
mEsptouchListener.onEsptouchResultAdded(esptouchResult);
}
}
}
}
private List<IEsptouchResult> __getEsptouchResultList() {
synchronized (mEsptouchResultList) {
if (mEsptouchResultList.isEmpty()) {
EsptouchResult esptouchResultFail = new EsptouchResult(false,
null, null);
esptouchResultFail.setIsCancelled(mIsCancelled.get());
mEsptouchResultList.add(esptouchResultFail);
}
return mEsptouchResultList;
}
}
private synchronized void __interrupt() {
if (!mIsInterrupt) {
mIsInterrupt = true;
mSocketClient.interrupt();
mSocketServer.interrupt();
// interrupt the current Thread which is used to wait for udp response
if (mTask != null) {
mTask.interrupt();
mTask = null;
}
}
}
@Override
public void interrupt() {
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "interrupt()");
}
mIsCancelled.set(true);
__interrupt();
}
private void __listenAsyn(final int expectDataLen) {
mTask = new Thread() {
public void run() {
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "__listenAsyn() start");
}
long startTimestamp = System.currentTimeMillis();
// byte[] apSsidAndPassword = ByteUtil.getBytesByString(mApSsid
// + mApPassword);
byte expectOneByte = (byte) (mApSsid.length + mApPassword.length + 9);
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "expectOneByte: " + (0 + expectOneByte));
}
byte receiveOneByte = -1;
byte[] receiveBytes = null;
while (mEsptouchResultList.size() < mParameter
.getExpectTaskResultCount() && !mIsInterrupt) {
receiveBytes = mSocketServer
.receiveSpecLenBytes(expectDataLen);
if (receiveBytes != null) {
receiveOneByte = receiveBytes[0];
} else {
receiveOneByte = -1;
}
if (receiveOneByte == expectOneByte) {
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "receive correct broadcast");
}
// change the socket's timeout
long consume = System.currentTimeMillis()
- startTimestamp;
int timeout = (int) (mParameter
.getWaitUdpTotalMillisecond() - consume);
if (timeout < 0) {
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "esptouch timeout");
}
break;
} else {
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "mSocketServer's new timeout is "
+ timeout + " milliseconds");
}
mSocketServer.setSoTimeout(timeout);
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "receive correct broadcast");
}
if (receiveBytes != null) {
String bssid = ByteUtil.parseBssid(
receiveBytes,
mParameter.getEsptouchResultOneLen(),
mParameter.getEsptouchResultMacLen());
InetAddress inetAddress = EspNetUtil
.parseInetAddr(
receiveBytes,
mParameter
.getEsptouchResultOneLen()
+ mParameter
.getEsptouchResultMacLen(),
mParameter
.getEsptouchResultIpLen());
__putEsptouchResult(true, bssid, inetAddress);
}
}
} else {
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "receive rubbish message, just ignore");
}
}
}
mIsSuc = mEsptouchResultList.size() >= mParameter
.getExpectTaskResultCount();
__EsptouchTask.this.__interrupt();
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "__listenAsyn() finish");
}
}
};
mTask.start();
}
private boolean __execute(IEsptouchGenerator generator) {
long startTime = System.currentTimeMillis();
long currentTime = startTime;
long lastTime = currentTime - mParameter.getTimeoutTotalCodeMillisecond();
byte[][] gcBytes2 = generator.getGCBytes2();
byte[][] dcBytes2 = generator.getDCBytes2();
int index = 0;
while (!mIsInterrupt) {
if (currentTime - lastTime >= mParameter.getTimeoutTotalCodeMillisecond()) {
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "send gc code ");
}
// send guide code
while (!mIsInterrupt
&& System.currentTimeMillis() - currentTime < mParameter
.getTimeoutGuideCodeMillisecond()) {
mSocketClient.sendData(gcBytes2,
mParameter.getTargetHostname(),
mParameter.getTargetPort(),
mParameter.getIntervalGuideCodeMillisecond());
// check whether the udp is send enough time
if (System.currentTimeMillis() - startTime > mParameter.getWaitUdpSendingMillisecond()) {
break;
}
}
lastTime = currentTime;
} else {
mSocketClient.sendData(dcBytes2, index, ONE_DATA_LEN,
mParameter.getTargetHostname(),
mParameter.getTargetPort(),
mParameter.getIntervalDataCodeMillisecond());
index = (index + ONE_DATA_LEN) % dcBytes2.length;
}
currentTime = System.currentTimeMillis();
// check whether the udp is send enough time
if (currentTime - startTime > mParameter.getWaitUdpSendingMillisecond()) {
break;
}
}
return mIsSuc;
}
private void __checkTaskValid() {
// !!!NOTE: the esptouch task could be executed only once
if (this.mIsExecuted) {
throw new IllegalStateException(
"the Esptouch task could be executed only once");
}
this.mIsExecuted = true;
}
@Override
public IEsptouchResult executeForResult() throws RuntimeException {
return executeForResults(1).get(0);
}
@Override
public boolean isCancelled() {
return this.mIsCancelled.get();
}
@Override
public List<IEsptouchResult> executeForResults(int expectTaskResultCount)
throws RuntimeException {
__checkTaskValid();
mParameter.setExpectTaskResultCount(expectTaskResultCount);
if (__IEsptouchTask.DEBUG) {
Log.d(TAG, "execute()");
}
if (Looper.myLooper() == Looper.getMainLooper()) {
throw new RuntimeException(
"Don't call the esptouch Task at Main(UI) thread directly.");
}
InetAddress localInetAddress = EspNetUtil.getLocalInetAddress(mContext);
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "localInetAddress: " + localInetAddress);
}
// generator the esptouch byte[][] to be transformed, which will cost
// some time(maybe a bit much)
IEsptouchGenerator generator = new EsptouchGenerator(mApSsid, mApBssid,
mApPassword, localInetAddress, mIsSsidHidden);
// listen the esptouch result asyn
__listenAsyn(mParameter.getEsptouchResultTotalLen());
boolean isSuc = false;
for (int i = 0; i < mParameter.getTotalRepeatTime(); i++) {
isSuc = __execute(generator);
if (isSuc) {
return __getEsptouchResultList();
}
}
if (!mIsInterrupt) {
// wait the udp response without sending udp broadcast
try {
Thread.sleep(mParameter.getWaitUdpReceivingMillisecond());
} catch (InterruptedException e) {
// receive the udp broadcast or the user interrupt the task
if (this.mIsSuc) {
return __getEsptouchResultList();
} else {
this.__interrupt();
return __getEsptouchResultList();
}
}
this.__interrupt();
}
return __getEsptouchResultList();
}
@Override
public void setEsptouchListener(IEsptouchListener esptouchListener) {
mEsptouchListener = esptouchListener;
}
}