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 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 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 __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 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; } }