添加安卓端基本功能

This commit is contained in:
kerwincui
2021-05-19 15:46:10 +08:00
parent 6ae09fc6dc
commit a7c6f99c6f
260 changed files with 16889 additions and 0 deletions

1
android/esptouch/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,42 @@
# EspTouch
[example](../app/src/main/java/com/espressif/esptouch/android/v1)
- Create task instance
```java
Context context; // Set Applicatioin context
byte[] apSsid = {}; // Set AP's SSID
byte[] apBssid = {}; // Set AP's BSSID
byte[] apPassword = {}; // Set AP's password
EsptouchTask task = new EsptouchTask(apSsid, apBssid, apPassword, context);
task.setPackageBroadcast(true); // if true send broadcast packets, else send multicast packets
```
- Set result callback
```java
task.setEsptouchListener(new IEsptouchListener() {
@Override
public void onEsptouchResultAdded(IEsptouchResult result) {
// Result callback
}
});
```
- Execute task
```java
int expectResultCount = 1;
List<IEsptouchResult> results = task.executeForResults(expectResultCount);
IEsptouchResult first = results.get(0);
if (first.isCancelled()) {
// User cancel the task
return;
}
if (first.isSuc()) {
// EspTouch successfully
}
```
- Cancel task
```java
task.interrupt();
```

View File

@@ -0,0 +1,31 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 14
targetSdkVersion 29
versionCode 8
versionName "v0.3.7.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}

21
android/esptouch/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.espressif.iot.esptouch" >
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
</manifest>

View File

@@ -0,0 +1,56 @@
package com.espressif.iot.esptouch;
import java.net.InetAddress;
import java.util.concurrent.atomic.AtomicBoolean;
public class EsptouchResult implements IEsptouchResult {
private final boolean mIsSuc;
private final String mBssid;
private final InetAddress mInetAddress;
private AtomicBoolean mIsCancelled;
/**
* Constructor of EsptouchResult
*
* @param isSuc whether the esptouch task is executed suc
* @param bssid the device's bssid
* @param inetAddress the device's ip address
*/
public EsptouchResult(boolean isSuc, String bssid, InetAddress inetAddress) {
this.mIsSuc = isSuc;
this.mBssid = bssid;
this.mInetAddress = inetAddress;
this.mIsCancelled = new AtomicBoolean(false);
}
@Override
public boolean isSuc() {
return this.mIsSuc;
}
@Override
public String getBssid() {
return this.mBssid;
}
@Override
public boolean isCancelled() {
return mIsCancelled.get();
}
public void setIsCancelled(boolean isCancelled) {
this.mIsCancelled.set(isCancelled);
}
@Override
public InetAddress getInetAddress() {
return this.mInetAddress;
}
@Override
public String toString() {
return String.format("bssid=%s, address=%s, suc=%b, cancel=%b", mBssid,
mInetAddress == null ? null : mInetAddress.getHostAddress(), mIsSuc, mIsCancelled.get());
}
}

View File

@@ -0,0 +1,115 @@
package com.espressif.iot.esptouch;
import android.content.Context;
import android.text.TextUtils;
import com.espressif.iot.esptouch.protocol.TouchData;
import com.espressif.iot.esptouch.security.ITouchEncryptor;
import com.espressif.iot.esptouch.task.EsptouchTaskParameter;
import com.espressif.iot.esptouch.task.__EsptouchTask;
import com.espressif.iot.esptouch.util.TouchNetUtil;
import java.util.List;
public class EsptouchTask implements IEsptouchTask {
private __EsptouchTask _mEsptouchTask;
private EsptouchTaskParameter _mParameter;
/**
* Constructor of EsptouchTask
*
* @param apSsid the Ap's ssid
* @param apBssid the Ap's bssid
* @param apPassword the Ap's password
* @param context the {@link Context} of the Application
*/
public EsptouchTask(String apSsid, String apBssid, String apPassword, Context context) {
this(apSsid, apBssid, apPassword, null, context);
}
/**
* Constructor of EsptouchTask
*
* @param apSsid the Ap's ssid
* @param apBssid the Ap's bssid
* @param apPassword the Ap's password
* @param context the {@link Context} of the Application
*/
public EsptouchTask(byte[] apSsid, byte[] apBssid, byte[] apPassword, Context context) {
this(apSsid, apBssid, apPassword, null, context);
}
private EsptouchTask(String apSsid, String apBssid, String apPassword, ITouchEncryptor encryptor, Context context) {
if (TextUtils.isEmpty(apSsid)) {
throw new NullPointerException("SSID can't be empty");
}
if (TextUtils.isEmpty(apBssid)) {
throw new NullPointerException("BSSID can't be empty");
}
if (apPassword == null) {
apPassword = "";
}
TouchData ssid = new TouchData(apSsid);
TouchData bssid = new TouchData(TouchNetUtil.parseBssid2bytes(apBssid));
if (bssid.getData().length != 6) {
throw new IllegalArgumentException("Bssid format must be aa:bb:cc:dd:ee:ff");
}
TouchData password = new TouchData(apPassword);
init(context, ssid, bssid, password, encryptor);
}
private EsptouchTask(byte[] apSsid, byte[] apBssid, byte[] apPassword, ITouchEncryptor encryptor, Context context) {
if (apSsid == null || apSsid.length == 0) {
throw new NullPointerException("SSID can't be empty");
}
if (apBssid == null || apBssid.length != 6) {
throw new NullPointerException("BSSID is empty or length is not 6");
}
if (apPassword == null) {
apPassword = new byte[0];
}
TouchData ssid = new TouchData(apSsid);
TouchData bssid = new TouchData(apBssid);
TouchData password = new TouchData(apPassword);
init(context, ssid, bssid, password, encryptor);
}
private void init(Context context, TouchData ssid, TouchData bssid, TouchData password, ITouchEncryptor encryptor) {
_mParameter = new EsptouchTaskParameter();
_mEsptouchTask = new __EsptouchTask(context, ssid, bssid, password, encryptor, _mParameter);
}
@Override
public void interrupt() {
_mEsptouchTask.interrupt();
}
@Override
public IEsptouchResult executeForResult() throws RuntimeException {
return _mEsptouchTask.executeForResult();
}
@Override
public boolean isCancelled() {
return _mEsptouchTask.isCancelled();
}
@Override
public List<IEsptouchResult> executeForResults(int expectTaskResultCount)
throws RuntimeException {
if (expectTaskResultCount <= 0) {
expectTaskResultCount = Integer.MAX_VALUE;
}
return _mEsptouchTask.executeForResults(expectTaskResultCount);
}
@Override
public void setEsptouchListener(IEsptouchListener esptouchListener) {
_mEsptouchTask.setEsptouchListener(esptouchListener);
}
@Override
public void setPackageBroadcast(boolean broadcast) {
_mParameter.setBroadcast(broadcast);
}
}

View File

@@ -0,0 +1,11 @@
package com.espressif.iot.esptouch;
public interface IEsptouchListener {
/**
* when new esptouch result is added, the listener will call
* onEsptouchResultAdded callback
*
* @param result the Esptouch result
*/
void onEsptouchResultAdded(IEsptouchResult result);
}

View File

@@ -0,0 +1,34 @@
package com.espressif.iot.esptouch;
import java.net.InetAddress;
public interface IEsptouchResult {
/**
* check whether the esptouch task is executed suc
*
* @return whether the esptouch task is executed suc
*/
boolean isSuc();
/**
* get the device's bssid
*
* @return the device's bssid
*/
String getBssid();
/**
* check whether the esptouch task is cancelled by user
*
* @return whether the esptouch task is cancelled by user
*/
boolean isCancelled();
/**
* get the ip address of the device
*
* @return the ip device of the device
*/
InetAddress getInetAddress();
}

View File

@@ -0,0 +1,61 @@
package com.espressif.iot.esptouch;
import java.util.List;
public interface IEsptouchTask {
String ESPTOUCH_VERSION = BuildConfig.VERSION_NAME;
/**
* set the esptouch listener, when one device is connected to the Ap, it will be called back
*
* @param esptouchListener when one device is connected to the Ap, it will be called back
*/
void setEsptouchListener(IEsptouchListener esptouchListener);
/**
* Interrupt the Esptouch Task when User tap back or close the Application.
*/
void interrupt();
/**
* Note: !!!Don't call the task at UI Main Thread or RuntimeException will
* be thrown Execute the Esptouch Task and return the result
* <p>
* Smart Config v2.4 support the API
*
* @return the IEsptouchResult
*/
IEsptouchResult executeForResult() throws RuntimeException;
/**
* Note: !!!Don't call the task at UI Main Thread or RuntimeException will
* be thrown Execute the Esptouch Task and return the result
* <p>
* Smart Config v2.4 support the API
* <p>
* It will be blocked until the client receive result count >= expectTaskResultCount.
* If it fail, it will return one fail result will be returned in the list.
* If it is cancelled while executing,
* if it has received some results, all of them will be returned in the list.
* if it hasn't received any results, one cancel result will be returned in the list.
*
* @param expectTaskResultCount the expect result count(if expectTaskResultCount <= 0,
* expectTaskResultCount = Integer.MAX_VALUE)
* @return the list of IEsptouchResult
*/
List<IEsptouchResult> executeForResults(int expectTaskResultCount) throws RuntimeException;
/**
* check whether the task is cancelled by user
*
* @return whether the task is cancelled by user
*/
boolean isCancelled();
/**
* Set broadcast or multicast when post configure info
*
* @param broadcast true is broadcast, false is multicast
*/
void setPackageBroadcast(boolean broadcast);
}

View File

@@ -0,0 +1,87 @@
package com.espressif.iot.esptouch.protocol;
import com.espressif.iot.esptouch.task.ICodeData;
import com.espressif.iot.esptouch.util.ByteUtil;
import com.espressif.iot.esptouch.util.CRC8;
/**
* one data format:(data code should have 2 to 65 data)
* <p>
* control byte high 4 bits low 4 bits
* 1st 9bits: 0x0 crc(high) data(high)
* 2nd 9bits: 0x1 sequence header
* 3rd 9bits: 0x0 crc(low) data(low)
* <p>
* sequence header: 0,1,2,...
*
* @author afunx
*/
public class DataCode implements ICodeData {
public static final int DATA_CODE_LEN = 6;
private static final int INDEX_MAX = 127;
private final byte mSeqHeader;
private final byte mDataHigh;
private final byte mDataLow;
// the crc here means the crc of the data and sequence header be transformed
// it is calculated by index and data to be transformed
private final byte mCrcHigh;
private final byte mCrcLow;
/**
* Constructor of DataCode
*
* @param u8 the character to be transformed
* @param index the index of the char
*/
public DataCode(char u8, int index) {
if (index > INDEX_MAX) {
throw new RuntimeException("index > INDEX_MAX");
}
byte[] dataBytes = ByteUtil.splitUint8To2bytes(u8);
mDataHigh = dataBytes[0];
mDataLow = dataBytes[1];
CRC8 crc8 = new CRC8();
crc8.update(ByteUtil.convertUint8toByte(u8));
crc8.update(index);
byte[] crcBytes = ByteUtil.splitUint8To2bytes((char) crc8.getValue());
mCrcHigh = crcBytes[0];
mCrcLow = crcBytes[1];
mSeqHeader = (byte) index;
}
@Override
public byte[] getBytes() {
byte[] dataBytes = new byte[DATA_CODE_LEN];
dataBytes[0] = 0x00;
dataBytes[1] = ByteUtil.combine2bytesToOne(mCrcHigh, mDataHigh);
dataBytes[2] = 0x01;
dataBytes[3] = mSeqHeader;
dataBytes[4] = 0x00;
dataBytes[5] = ByteUtil.combine2bytesToOne(mCrcLow, mDataLow);
return dataBytes;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
byte[] dataBytes = getBytes();
for (int i = 0; i < DATA_CODE_LEN; i++) {
String hexString = ByteUtil.convertByte2HexString(dataBytes[i]);
sb.append("0x");
if (hexString.length() == 1) {
sb.append("0");
}
sb.append(hexString).append(" ");
}
return sb.toString();
}
@Override
public char[] getU8s() {
throw new RuntimeException("DataCode don't support getU8s()");
}
}

View File

@@ -0,0 +1,141 @@
package com.espressif.iot.esptouch.protocol;
import com.espressif.iot.esptouch.security.ITouchEncryptor;
import com.espressif.iot.esptouch.task.ICodeData;
import com.espressif.iot.esptouch.util.ByteUtil;
import com.espressif.iot.esptouch.util.CRC8;
import java.net.InetAddress;
import java.util.LinkedList;
public class DatumCode implements ICodeData {
// define by the Esptouch protocol, all of the datum code should add 1 at last to prevent 0
private static final int EXTRA_LEN = 40;
private static final int EXTRA_HEAD_LEN = 5;
private final LinkedList<DataCode> mDataCodes;
/**
* Constructor of DatumCode
*
* @param apSsid the Ap's ssid
* @param apBssid the Ap's bssid
* @param apPassword the Ap's password
* @param ipAddress the ip address of the phone or pad
* @param encryptor null use origin data, not null use encrypted data
*/
public DatumCode(byte[] apSsid, byte[] apBssid, byte[] apPassword,
InetAddress ipAddress, ITouchEncryptor encryptor) {
// Data = total len(1 byte) + apPwd len(1 byte) + SSID CRC(1 byte) +
// BSSID CRC(1 byte) + TOTAL XOR(1 byte)+ ipAddress(4 byte) + apPwd + apSsid apPwdLen <=
// 105 at the moment
// total xor
char totalXor = 0;
char apPwdLen = (char) apPassword.length;
CRC8 crc = new CRC8();
crc.update(apSsid);
char apSsidCrc = (char) crc.getValue();
crc.reset();
crc.update(apBssid);
char apBssidCrc = (char) crc.getValue();
char apSsidLen = (char) apSsid.length;
byte[] ipBytes = ipAddress.getAddress();
int ipLen = ipBytes.length;
char totalLen = (char) (EXTRA_HEAD_LEN + ipLen + apPwdLen + apSsidLen);
// build data codes
mDataCodes = new LinkedList<>();
mDataCodes.add(new DataCode(totalLen, 0));
totalXor ^= totalLen;
mDataCodes.add(new DataCode(apPwdLen, 1));
totalXor ^= apPwdLen;
mDataCodes.add(new DataCode(apSsidCrc, 2));
totalXor ^= apSsidCrc;
mDataCodes.add(new DataCode(apBssidCrc, 3));
totalXor ^= apBssidCrc;
// ESPDataCode 4 is null
for (int i = 0; i < ipLen; ++i) {
char c = ByteUtil.convertByte2Uint8(ipBytes[i]);
totalXor ^= c;
mDataCodes.add(new DataCode(c, i + EXTRA_HEAD_LEN));
}
for (int i = 0; i < apPassword.length; i++) {
char c = ByteUtil.convertByte2Uint8(apPassword[i]);
totalXor ^= c;
mDataCodes.add(new DataCode(c, i + EXTRA_HEAD_LEN + ipLen));
}
// totalXor will xor apSsidChars no matter whether the ssid is hidden
for (int i = 0; i < apSsid.length; i++) {
char c = ByteUtil.convertByte2Uint8(apSsid[i]);
totalXor ^= c;
mDataCodes.add(new DataCode(c, i + EXTRA_HEAD_LEN + ipLen + apPwdLen));
}
// add total xor last
mDataCodes.add(4, new DataCode(totalXor, 4));
// add bssid
int bssidInsertIndex = EXTRA_HEAD_LEN;
for (int i = 0; i < apBssid.length; i++) {
int index = totalLen + i;
char c = ByteUtil.convertByte2Uint8(apBssid[i]);
DataCode dc = new DataCode(c, index);
if (bssidInsertIndex >= mDataCodes.size()) {
mDataCodes.add(dc);
} else {
mDataCodes.add(bssidInsertIndex, dc);
}
bssidInsertIndex += 4;
}
}
@Override
public byte[] getBytes() {
byte[] datumCode = new byte[mDataCodes.size() * DataCode.DATA_CODE_LEN];
int index = 0;
for (DataCode dc : mDataCodes) {
for (byte b : dc.getBytes()) {
datumCode[index++] = b;
}
}
return datumCode;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
byte[] dataBytes = getBytes();
for (byte dataByte : dataBytes) {
String hexString = ByteUtil.convertByte2HexString(dataByte);
sb.append("0x");
if (hexString.length() == 1) {
sb.append("0");
}
sb.append(hexString).append(" ");
}
return sb.toString();
}
@Override
public char[] getU8s() {
byte[] dataBytes = getBytes();
int len = dataBytes.length / 2;
char[] dataU8s = new char[len];
byte high, low;
for (int i = 0; i < len; i++) {
high = dataBytes[i * 2];
low = dataBytes[i * 2 + 1];
dataU8s[i] = (char) (ByteUtil.combine2bytesToU16(high, low) + EXTRA_LEN);
}
return dataU8s;
}
}

View File

@@ -0,0 +1,54 @@
package com.espressif.iot.esptouch.protocol;
import com.espressif.iot.esptouch.security.ITouchEncryptor;
import com.espressif.iot.esptouch.task.IEsptouchGenerator;
import com.espressif.iot.esptouch.util.ByteUtil;
import java.net.InetAddress;
public class EsptouchGenerator implements IEsptouchGenerator {
private final byte[][] mGcBytes2;
private final byte[][] mDcBytes2;
/**
* Constructor of EsptouchGenerator, it will cost some time(maybe a bit
* much)
*
* @param apSsid the Ap's ssid
* @param apBssid the Ap's bssid
* @param apPassword the Ap's password
* @param inetAddress the phone's or pad's local ip address allocated by Ap
*/
public EsptouchGenerator(byte[] apSsid, byte[] apBssid, byte[] apPassword, InetAddress inetAddress,
ITouchEncryptor encryptor) {
// generate guide code
GuideCode gc = new GuideCode();
char[] gcU81 = gc.getU8s();
mGcBytes2 = new byte[gcU81.length][];
for (int i = 0; i < mGcBytes2.length; i++) {
mGcBytes2[i] = ByteUtil.genSpecBytes(gcU81[i]);
}
// generate data code
DatumCode dc = new DatumCode(apSsid, apBssid, apPassword, inetAddress, encryptor);
char[] dcU81 = dc.getU8s();
mDcBytes2 = new byte[dcU81.length][];
for (int i = 0; i < mDcBytes2.length; i++) {
mDcBytes2[i] = ByteUtil.genSpecBytes(dcU81[i]);
}
}
@Override
public byte[][] getGCBytes2() {
return mGcBytes2;
}
@Override
public byte[][] getDCBytes2() {
return mDcBytes2;
}
}

View File

@@ -0,0 +1,39 @@
package com.espressif.iot.esptouch.protocol;
import com.espressif.iot.esptouch.task.ICodeData;
import com.espressif.iot.esptouch.util.ByteUtil;
public class GuideCode implements ICodeData {
public static final int GUIDE_CODE_LEN = 4;
@Override
public byte[] getBytes() {
throw new RuntimeException("DataCode don't support getBytes()");
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
char[] dataU8s = getU8s();
for (int i = 0; i < GUIDE_CODE_LEN; i++) {
String hexString = ByteUtil.convertU8ToHexString(dataU8s[i]);
sb.append("0x");
if (hexString.length() == 1) {
sb.append("0");
}
sb.append(hexString).append(" ");
}
return sb.toString();
}
@Override
public char[] getU8s() {
char[] guidesU8s = new char[GUIDE_CODE_LEN];
guidesU8s[0] = 515;
guidesU8s[1] = 514;
guidesU8s[2] = 513;
guidesU8s[3] = 512;
return guidesU8s;
}
}

View File

@@ -0,0 +1,22 @@
package com.espressif.iot.esptouch.protocol;
import com.espressif.iot.esptouch.util.ByteUtil;
public class TouchData {
private final byte[] mData;
public TouchData(String string) {
mData = ByteUtil.getBytesByString(string);
}
public TouchData(byte[] data) {
if (data == null) {
throw new NullPointerException("data can't be null");
}
mData = data;
}
public byte[] getData() {
return mData;
}
}

View File

@@ -0,0 +1,5 @@
package com.espressif.iot.esptouch.security;
public interface ITouchEncryptor {
byte[] encrypt(byte[] src);
}

View File

@@ -0,0 +1,104 @@
package com.espressif.iot.esptouch.security;
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;
public class TouchAES implements ITouchEncryptor {
private static final String TRANSFORMATION_DEFAULT = "AES/ECB/PKCS5Padding";
private final byte[] mKey;
private final byte[] mIV;
private final String mTransformation;
private Cipher mEncryptCipher;
private Cipher mDecryptCipher;
public TouchAES(byte[] key) {
this(key, null, TRANSFORMATION_DEFAULT);
}
public TouchAES(byte[] key, String transformation) {
this(key, null, transformation);
}
public TouchAES(byte[] key, byte[] iv) {
this(key, iv, TRANSFORMATION_DEFAULT);
}
public TouchAES(byte[] key, byte[] iv, String transformation) {
mKey = key;
mIV = iv;
mTransformation = transformation;
mEncryptCipher = createEncryptCipher();
mDecryptCipher = createDecryptCipher();
}
private Cipher createEncryptCipher() {
try {
Cipher cipher = Cipher.getInstance(mTransformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(mKey, "AES");
if (mIV == null) {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
} else {
IvParameterSpec parameterSpec = new IvParameterSpec(mIV);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, parameterSpec);
}
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException
e) {
e.printStackTrace();
}
return null;
}
private Cipher createDecryptCipher() {
try {
Cipher cipher = Cipher.getInstance(mTransformation);
SecretKeySpec secretKeySpec = new SecretKeySpec(mKey, "AES");
if (mIV == null) {
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
} else {
IvParameterSpec parameterSpec = new IvParameterSpec(mIV);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, parameterSpec);
}
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException
e) {
e.printStackTrace();
}
return null;
}
public byte[] encrypt(byte[] content) {
try {
return mEncryptCipher.doFinal(content);
} catch (BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
public byte[] decrypt(byte[] content) {
try {
return mDecryptCipher.doFinal(content);
} catch (BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -0,0 +1,165 @@
package com.espressif.iot.esptouch.task;
public class EsptouchTaskParameter implements IEsptouchTaskParameter {
private static int _datagramCount = 0;
private long mIntervalGuideCodeMillisecond;
private long mIntervalDataCodeMillisecond;
private long mTimeoutGuideCodeMillisecond;
private long mTimeoutDataCodeMillisecond;
private int mTotalRepeatTime;
private int mEsptouchResultOneLen;
private int mEsptouchResultMacLen;
private int mEsptouchResultIpLen;
private int mEsptouchResultTotalLen;
private int mPortListening;
private int mTargetPort;
private int mWaitUdpReceivingMilliseond;
private int mWaitUdpSendingMillisecond;
private int mThresholdSucBroadcastCount;
private int mExpectTaskResultCount;
private boolean mBroadcast = true;
public EsptouchTaskParameter() {
mIntervalGuideCodeMillisecond = 8;
mIntervalDataCodeMillisecond = 8;
mTimeoutGuideCodeMillisecond = 2000;
mTimeoutDataCodeMillisecond = 4000;
mTotalRepeatTime = 1;
mEsptouchResultOneLen = 1;
mEsptouchResultMacLen = 6;
mEsptouchResultIpLen = 4;
mEsptouchResultTotalLen = 1 + 6 + 4;
mPortListening = 18266;
mTargetPort = 7001;
mWaitUdpReceivingMilliseond = 15000;
mWaitUdpSendingMillisecond = 45000;
mThresholdSucBroadcastCount = 1;
mExpectTaskResultCount = 1;
}
// the range of the result should be 1-100
private static int __getNextDatagramCount() {
return 1 + (_datagramCount++) % 100;
}
@Override
public long getIntervalGuideCodeMillisecond() {
return mIntervalGuideCodeMillisecond;
}
@Override
public long getIntervalDataCodeMillisecond() {
return mIntervalDataCodeMillisecond;
}
@Override
public long getTimeoutGuideCodeMillisecond() {
return mTimeoutGuideCodeMillisecond;
}
@Override
public long getTimeoutDataCodeMillisecond() {
return mTimeoutDataCodeMillisecond;
}
@Override
public long getTimeoutTotalCodeMillisecond() {
return mTimeoutGuideCodeMillisecond + mTimeoutDataCodeMillisecond;
}
@Override
public int getTotalRepeatTime() {
return mTotalRepeatTime;
}
@Override
public int getEsptouchResultOneLen() {
return mEsptouchResultOneLen;
}
@Override
public int getEsptouchResultMacLen() {
return mEsptouchResultMacLen;
}
@Override
public int getEsptouchResultIpLen() {
return mEsptouchResultIpLen;
}
@Override
public int getEsptouchResultTotalLen() {
return mEsptouchResultTotalLen;
}
@Override
public int getPortListening() {
return mPortListening;
}
// target hostname is : 234.1.1.1, 234.2.2.2, 234.3.3.3 to 234.100.100.100
@Override
public String getTargetHostname() {
if (mBroadcast) {
return "255.255.255.255";
} else {
int count = __getNextDatagramCount();
return "234." + count + "." + count + "." + count;
}
}
@Override
public int getTargetPort() {
return mTargetPort;
}
@Override
public int getWaitUdpReceivingMillisecond() {
return mWaitUdpReceivingMilliseond;
}
@Override
public int getWaitUdpSendingMillisecond() {
return mWaitUdpSendingMillisecond;
}
@Override
public int getWaitUdpTotalMillisecond() {
return mWaitUdpReceivingMilliseond + mWaitUdpSendingMillisecond;
}
@Override
public void setWaitUdpTotalMillisecond(int waitUdpTotalMillisecond) {
if (waitUdpTotalMillisecond < mWaitUdpReceivingMilliseond
+ getTimeoutTotalCodeMillisecond()) {
// if it happen, even one turn about sending udp broadcast can't be
// completed
throw new IllegalArgumentException(
"waitUdpTotalMillisecod is invalid, "
+ "it is less than mWaitUdpReceivingMilliseond + getTimeoutTotalCodeMillisecond()");
}
mWaitUdpSendingMillisecond = waitUdpTotalMillisecond
- mWaitUdpReceivingMilliseond;
}
@Override
public int getThresholdSucBroadcastCount() {
return mThresholdSucBroadcastCount;
}
@Override
public int getExpectTaskResultCount() {
return this.mExpectTaskResultCount;
}
@Override
public void setExpectTaskResultCount(int expectTaskResultCount) {
this.mExpectTaskResultCount = expectTaskResultCount;
}
@Override
public void setBroadcast(boolean broadcast) {
mBroadcast = broadcast;
}
}

View File

@@ -0,0 +1,22 @@
package com.espressif.iot.esptouch.task;
/**
* the class used to represent some code to be transformed by UDP socket should implement the interface
*
* @author afunx
*/
public interface ICodeData {
/**
* Get the byte[] to be transformed.
*
* @return the byte[] to be transfromed
*/
byte[] getBytes();
/**
* Get the char[](u8[]) to be transfromed.
*
* @return the char[](u8) to be transformed
*/
char[] getU8s();
}

View File

@@ -0,0 +1,17 @@
package com.espressif.iot.esptouch.task;
public interface IEsptouchGenerator {
/**
* Get guide code by the format of byte[][]
*
* @return guide code by the format of byte[][]
*/
byte[][] getGCBytes2();
/**
* Get data code by the format of byte[][]
*
* @return data code by the format of byte[][]
*/
byte[][] getDCBytes2();
}

View File

@@ -0,0 +1,156 @@
package com.espressif.iot.esptouch.task;
public interface IEsptouchTaskParameter {
/**
* get interval millisecond for guide code(the time between each guide code sending)
*
* @return interval millisecond for guide code(the time between each guide code sending)
*/
long getIntervalGuideCodeMillisecond();
/**
* get interval millisecond for data code(the time between each data code sending)
*
* @return interval millisecond for data code(the time between each data code sending)
*/
long getIntervalDataCodeMillisecond();
/**
* get timeout millisecond for guide code(the time how much the guide code sending)
*
* @return timeout millisecond for guide code(the time how much the guide code sending)
*/
long getTimeoutGuideCodeMillisecond();
/**
* get timeout millisecond for data code(the time how much the data code sending)
*
* @return timeout millisecond for data code(the time how much the data code sending)
*/
long getTimeoutDataCodeMillisecond();
/**
* get timeout millisecond for total code(guide code and data code altogether)
*
* @return timeout millisecond for total code(guide code and data code altogether)
*/
long getTimeoutTotalCodeMillisecond();
/**
* get total repeat time for executing esptouch task
*
* @return total repeat time for executing esptouch task
*/
int getTotalRepeatTime();
/**
* the length of the Esptouch result 1st byte is the total length of ssid and
* password, the other 6 bytes are the device's bssid
*/
/**
* get esptouchResult length of one
*
* @return length of one
*/
int getEsptouchResultOneLen();
/**
* get esptouchResult length of mac
*
* @return length of mac
*/
int getEsptouchResultMacLen();
/**
* get esptouchResult length of ip
*
* @return length of ip
*/
int getEsptouchResultIpLen();
/**
* get esptouchResult total length
*
* @return total length
*/
int getEsptouchResultTotalLen();
/**
* get port for listening(used by server)
*
* @return port for listening(used by server)
*/
int getPortListening();
/**
* get target hostname
*
* @return target hostame(used by client)
*/
String getTargetHostname();
/**
* get target port
*
* @return target port(used by client)
*/
int getTargetPort();
/**
* get millisecond for waiting udp receiving(receiving without sending)
*
* @return millisecond for waiting udp receiving(receiving without sending)
*/
int getWaitUdpReceivingMillisecond();
/**
* get millisecond for waiting udp sending(sending including receiving)
*
* @return millisecond for waiting udep sending(sending including receiving)
*/
int getWaitUdpSendingMillisecond();
/**
* get millisecond for waiting udp sending and receiving
*
* @return millisecond for waiting udp sending and receiving
*/
int getWaitUdpTotalMillisecond();
/**
* set the millisecond for waiting udp sending and receiving
*
* @param waitUdpTotalMillisecond the millisecond for waiting udp sending and receiving
*/
void setWaitUdpTotalMillisecond(int waitUdpTotalMillisecond);
/**
* get the threshold for how many correct broadcast should be received
*
* @return the threshold for how many correct broadcast should be received
*/
int getThresholdSucBroadcastCount();
/**
* get the count of expect task results
*
* @return the count of expect task results
*/
int getExpectTaskResultCount();
/**
* set the count of expect task results
*
* @param expectTaskResultCount the count of expect task results
*/
void setExpectTaskResultCount(int expectTaskResultCount);
/**
* Set broadcast or multicast
*
* @param broadcast true is broadcast, false is multicast
*/
void setBroadcast(boolean broadcast);
}

View File

@@ -0,0 +1,351 @@
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.security.ITouchEncryptor;
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.TouchNetUtil;
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 ITouchEncryptor mEncryptor;
private final Context mContext;
private final 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,
ITouchEncryptor encryptor, IEsptouchTaskParameter parameter) {
Log.i(TAG, "Welcome Esptouch " + IEsptouchTask.ESPTOUCH_VERSION);
mContext = context;
mEncryptor = encryptor;
mApSsid = apSsid.getData();
mApPassword = apPassword.getData();
mApBssid = apBssid.getData();
mIsCancelled = new AtomicBoolean(false);
mSocketClient = new UDPSocketClient();
mParameter = parameter;
mSocketServer = new UDPSocketServer(mParameter.getPortListening(),
mParameter.getWaitUdpTotalMillisecond(), context);
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 " +
"bssid = " + bssid + " , address = " + inetAddress);
}
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: " + 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 = TouchNetUtil
.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 = TouchNetUtil.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, mEncryptor);
// 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;
}
}

View File

@@ -0,0 +1,55 @@
package com.espressif.iot.esptouch.task;
import com.espressif.iot.esptouch.IEsptouchListener;
import com.espressif.iot.esptouch.IEsptouchResult;
import java.util.List;
/**
* IEsptouchTask defined the task of esptouch should offer. INTERVAL here means
* the milliseconds of interval of the step. REPEAT here means the repeat times
* of the step.
*
* @author afunx
*/
public interface __IEsptouchTask {
/**
* Turn on or off the log.
*/
static final boolean DEBUG = true;
/**
* set the esptouch listener, when one device is connected to the Ap, it will be called back
*
* @param esptouchListener when one device is connected to the Ap, it will be called back
*/
void setEsptouchListener(IEsptouchListener esptouchListener);
/**
* Interrupt the Esptouch Task when User tap back or close the Application.
*/
void interrupt();
/**
* Note: !!!Don't call the task at UI Main Thread or RuntimeException will
* be thrown Execute the Esptouch Task and return the result
*
* @return the IEsptouchResult
* @throws RuntimeException
*/
IEsptouchResult executeForResult() throws RuntimeException;
/**
* Note: !!!Don't call the task at UI Main Thread or RuntimeException will
* be thrown Execute the Esptouch Task and return the result
*
* @param expectTaskResultCount the expect result count(if expectTaskResultCount <= 0,
* expectTaskResultCount = Integer.MAX_VALUE)
* @return the list of IEsptouchResult
* @throws RuntimeException
*/
List<IEsptouchResult> executeForResults(int expectTaskResultCount) throws RuntimeException;
boolean isCancelled();
}

View File

@@ -0,0 +1,130 @@
package com.espressif.iot.esptouch.udp;
import android.util.Log;
import com.espressif.iot.esptouch.task.__IEsptouchTask;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
/**
* this class is used to help send UDP data according to length
*
* @author afunx
*/
public class UDPSocketClient {
private static final String TAG = "UDPSocketClient";
private DatagramSocket mSocket;
private volatile boolean mIsStop;
private volatile boolean mIsClosed;
public UDPSocketClient() {
try {
this.mSocket = new DatagramSocket();
this.mIsStop = false;
this.mIsClosed = false;
} catch (SocketException e) {
if (__IEsptouchTask.DEBUG) {
Log.w(TAG, "SocketException");
}
e.printStackTrace();
}
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
public void interrupt() {
if (__IEsptouchTask.DEBUG) {
Log.i(TAG, "USPSocketClient is interrupt");
}
this.mIsStop = true;
}
/**
* close the UDP socket
*/
public synchronized void close() {
if (!this.mIsClosed) {
this.mSocket.close();
this.mIsClosed = true;
}
}
/**
* send the data by UDP
*
* @param data the data to be sent
* @param targetPort the port of target
* @param interval the milliseconds to between each UDP sent
*/
public void sendData(byte[][] data, String targetHostName, int targetPort,
long interval) {
sendData(data, 0, data.length, targetHostName, targetPort, interval);
}
/**
* send the data by UDP
*
* @param data the data to be sent
* @param offset the offset which data to be sent
* @param count the count of the data
* @param targetPort the port of target
* @param interval the milliseconds to between each UDP sent
*/
public void sendData(byte[][] data, int offset, int count,
String targetHostName, int targetPort, long interval) {
if ((data == null) || (data.length <= 0)) {
if (__IEsptouchTask.DEBUG) {
Log.w(TAG, "sendData(): data == null or length <= 0");
}
return;
}
for (int i = offset; !mIsStop && i < offset + count; i++) {
if (data[i].length == 0) {
continue;
}
try {
InetAddress targetInetAddress = InetAddress.getByName(targetHostName);
DatagramPacket localDatagramPacket = new DatagramPacket(
data[i], data[i].length, targetInetAddress, targetPort);
this.mSocket.send(localDatagramPacket);
} catch (UnknownHostException e) {
if (__IEsptouchTask.DEBUG) {
Log.w(TAG, "sendData(): UnknownHostException");
}
e.printStackTrace();
mIsStop = true;
break;
} catch (IOException e) {
if (__IEsptouchTask.DEBUG) {
Log.w(TAG, "sendData(): IOException, but just ignore it");
}
// for the Ap will make some troubles when the phone send too many UDP packets,
// but we don't expect the UDP packet received by others, so just ignore it
}
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
e.printStackTrace();
if (__IEsptouchTask.DEBUG) {
Log.w(TAG, "sendData is Interrupted");
}
mIsStop = true;
break;
}
}
if (mIsStop) {
close();
}
}
}

View File

@@ -0,0 +1,149 @@
package com.espressif.iot.esptouch.udp;
import android.content.Context;
import android.net.wifi.WifiManager;
import android.util.Log;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Arrays;
public class UDPSocketServer {
private static final String TAG = "UDPSocketServer";
private DatagramSocket mServerSocket;
private Context mContext;
private WifiManager.MulticastLock mLock;
private volatile boolean mIsClosed;
/**
* Constructor of UDP Socket Server
*
* @param port the Socket Server port
* @param socketTimeout the socket read timeout
* @param context the context of the Application
*/
public UDPSocketServer(int port, int socketTimeout, Context context) {
this.mContext = context;
try {
this.mServerSocket = new DatagramSocket(null);
this.mServerSocket.setReuseAddress(true);
this.mServerSocket.bind(new InetSocketAddress(port));
this.mServerSocket.setSoTimeout(socketTimeout);
} catch (IOException e) {
Log.w(TAG, "IOException");
e.printStackTrace();
}
this.mIsClosed = false;
WifiManager manager = (WifiManager) mContext.getApplicationContext()
.getSystemService(Context.WIFI_SERVICE);
mLock = manager.createMulticastLock("test wifi");
Log.d(TAG, "mServerSocket is created, socket read timeout: "
+ socketTimeout + ", port: " + port);
}
private synchronized void acquireLock() {
if (mLock != null && !mLock.isHeld()) {
mLock.acquire();
}
}
private synchronized void releaseLock() {
if (mLock != null && mLock.isHeld()) {
try {
mLock.release();
} catch (Throwable th) {
// ignoring this exception, probably wakeLock was already released
}
}
}
/**
* Set the socket timeout in milliseconds
*
* @param timeout the timeout in milliseconds or 0 for no timeout.
* @return true whether the timeout is set suc
*/
public boolean setSoTimeout(int timeout) {
try {
this.mServerSocket.setSoTimeout(timeout);
return true;
} catch (SocketException e) {
e.printStackTrace();
}
return false;
}
/**
* Receive one byte from the port and convert it into String
*
* @return
*/
public byte receiveOneByte() {
Log.d(TAG, "receiveOneByte() entrance");
try {
acquireLock();
DatagramPacket packet = new DatagramPacket(new byte[1], 1);
mServerSocket.receive(packet);
Log.d(TAG, "receive: " + (packet.getData()[0]));
return packet.getData()[0];
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
/**
* Receive specific length bytes from the port and convert it into String
* 21,24,-2,52,-102,-93,-60
* 15,18,fe,34,9a,a3,c4
*
* @return
*/
public byte[] receiveSpecLenBytes(int len) {
Log.d(TAG, "receiveSpecLenBytes() entrance: len = " + len);
try {
acquireLock();
DatagramPacket packet = new DatagramPacket(new byte[64], 64);
mServerSocket.receive(packet);
byte[] recDatas = Arrays.copyOf(packet.getData(), packet.getLength());
Log.d(TAG, "received len : " + recDatas.length);
for (int i = 0; i < recDatas.length; i++) {
Log.w(TAG, "recDatas[" + i + "]:" + recDatas[i]);
}
Log.w(TAG, "receiveSpecLenBytes: " + new String(recDatas));
if (recDatas.length != len) {
Log.w(TAG,
"received len is different from specific len, return null");
return null;
}
return recDatas;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void interrupt() {
Log.i(TAG, "USPSocketServer is interrupt");
close();
}
public synchronized void close() {
if (!this.mIsClosed) {
Log.w(TAG, "mServerSocket is closed");
mServerSocket.close();
releaseLock();
this.mIsClosed = true;
}
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
}

View File

@@ -0,0 +1,323 @@
package com.espressif.iot.esptouch.util;
import java.io.UnsupportedEncodingException;
import java.util.Random;
/**
* In Java, it don't support unsigned int, so we use char to replace uint8.
* The range of byte is [-128,127], and the range of char is [0,65535].
* So the byte could used to store the uint8.
* (We assume that the String could be mapped to assic)
*
* @author afunx
*/
public class ByteUtil {
public static final String ESPTOUCH_ENCODING_CHARSET = "UTF-8";
/**
* Put String to byte[]
*
* @param destbytes the byte[] of dest
* @param srcString the String of src
* @param destOffset the offset of byte[]
* @param srcOffset the offset of String
* @param count the count of dest, and the count of src as well
*/
public static void putString2bytes(byte[] destbytes, String srcString,
int destOffset, int srcOffset, int count) {
for (int i = 0; i < count; i++) {
destbytes[count + i] = srcString.getBytes()[i];
}
}
/**
* Convert uint8 into char( we treat char as uint8)
*
* @param uint8 the unit8 to be converted
* @return the byte of the unint8
*/
public static byte convertUint8toByte(char uint8) {
if (uint8 > Byte.MAX_VALUE - Byte.MIN_VALUE) {
throw new RuntimeException("Out of Boundary");
}
return (byte) uint8;
}
/**
* Convert char into uint8( we treat char as uint8 )
*
* @param b the byte to be converted
* @return the char(uint8)
*/
public static char convertByte2Uint8(byte b) {
// char will be promoted to int for char don't support & operator
// & 0xff could make negatvie value to positive
return (char) (b & 0xff);
}
/**
* Convert byte[] into char[]( we treat char[] as uint8[])
*
* @param bytes the byte[] to be converted
* @return the char[](uint8[])
*/
public static char[] convertBytes2Uint8s(byte[] bytes) {
int len = bytes.length;
char[] uint8s = new char[len];
for (int i = 0; i < len; i++) {
uint8s[i] = convertByte2Uint8(bytes[i]);
}
return uint8s;
}
/**
* Put byte[] into char[]( we treat char[] as uint8[])
*
* @param destUint8s the char[](uint8[]) array
* @param srcBytes the byte[]
* @param destOffset the offset of char[](uint8[])
* @param srcOffset the offset of byte[]
* @param count the count of dest, and the count of src as well
*/
public static void putbytes2Uint8s(char[] destUint8s, byte[] srcBytes,
int destOffset, int srcOffset, int count) {
for (int i = 0; i < count; i++) {
destUint8s[destOffset + i] = convertByte2Uint8(srcBytes[srcOffset
+ i]);
}
}
/**
* Convert byte to Hex String
*
* @param b the byte to be converted
* @return the Hex String
*/
public static String convertByte2HexString(byte b) {
char u8 = convertByte2Uint8(b);
return Integer.toHexString(u8);
}
/**
* Convert char(uint8) to Hex String
*
* @param u8 the char(uint8) to be converted
* @return the Hex String
*/
public static String convertU8ToHexString(char u8) {
return Integer.toHexString(u8);
}
/**
* Split uint8 to 2 bytes of high byte and low byte. e.g. 20 = 0x14 should
* be split to [0x01,0x04] 0x01 is high byte and 0x04 is low byte
*
* @param uint8 the char(uint8)
* @return the high and low bytes be split, byte[0] is high and byte[1] is
* low
*/
public static byte[] splitUint8To2bytes(char uint8) {
if (uint8 < 0 || uint8 > 0xff) {
throw new RuntimeException("Out of Boundary");
}
String hexString = Integer.toHexString(uint8);
byte low;
byte high;
if (hexString.length() > 1) {
high = (byte) Integer.parseInt(hexString.substring(0, 1), 16);
low = (byte) Integer.parseInt(hexString.substring(1, 2), 16);
} else {
high = 0;
low = (byte) Integer.parseInt(hexString.substring(0, 1), 16);
}
byte[] result = new byte[]{high, low};
return result;
}
/**
* Combine 2 bytes (high byte and low byte) to one whole byte
*
* @param high the high byte
* @param low the low byte
* @return the whole byte
*/
public static byte combine2bytesToOne(byte high, byte low) {
if (high < 0 || high > 0xf || low < 0 || low > 0xf) {
throw new RuntimeException("Out of Boundary");
}
return (byte) (high << 4 | low);
}
/**
* Combine 2 bytes (high byte and low byte) to
*
* @param high the high byte
* @param low the low byte
* @return the char(u8)
*/
public static char combine2bytesToU16(byte high, byte low) {
char highU8 = convertByte2Uint8(high);
char lowU8 = convertByte2Uint8(low);
return (char) (highU8 << 8 | lowU8);
}
/**
* Generate the random byte to be sent
*
* @return the random byte
*/
private static byte randomByte() {
return (byte) (127 - new Random().nextInt(256));
}
/**
* Generate the random byte to be sent
*
* @param len the len presented by u8
* @return the byte[] to be sent
*/
public static byte[] randomBytes(char len) {
byte[] data = new byte[len];
for (int i = 0; i < len; i++) {
data[i] = randomByte();
}
return data;
}
public static byte[] genSpecBytes(char len) {
byte[] data = new byte[len];
for (int i = 0; i < len; i++) {
data[i] = '1';
}
return data;
}
/**
* Generate the random byte to be sent
*
* @param len the len presented by byte
* @return the byte[] to be sent
*/
public static byte[] randomBytes(byte len) {
char u8 = convertByte2Uint8(len);
return randomBytes(u8);
}
/**
* Generate the specific byte to be sent
*
* @param len the len presented by byte
* @return the byte[]
*/
public static byte[] genSpecBytes(byte len) {
char u8 = convertByte2Uint8(len);
return genSpecBytes(u8);
}
public static String parseBssid(byte[] bssidBytes, int offset, int count) {
byte[] bytes = new byte[count];
System.arraycopy(bssidBytes, offset, bytes, 0, count);
return parseBssid(bytes);
}
/**
* parse "24,-2,52,-102,-93,-60" to "18,fe,34,9a,a3,c4"
* parse the bssid from hex to String
*
* @param bssidBytes the hex bytes bssid, e.g. {24,-2,52,-102,-93,-60}
* @return the String of bssid, e.g. 18fe349aa3c4
*/
public static String parseBssid(byte[] bssidBytes) {
StringBuilder sb = new StringBuilder();
int k;
String hexK;
String str;
for (byte bssidByte : bssidBytes) {
k = 0xff & bssidByte;
hexK = Integer.toHexString(k);
str = ((k < 16) ? ("0" + hexK) : (hexK));
System.out.println(str);
sb.append(str);
}
return sb.toString();
}
/**
* @param string the string to be used
* @return the byte[] of String according to {@link #ESPTOUCH_ENCODING_CHARSET}
*/
public static byte[] getBytesByString(String string) {
try {
return string.getBytes(ESPTOUCH_ENCODING_CHARSET);
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException("the charset is invalid");
}
}
private static void test_splitUint8To2bytes() {
// 20 = 0x14
byte[] result = splitUint8To2bytes((char) 20);
if (result[0] == 1 && result[1] == 4) {
System.out.println("test_splitUint8To2bytes(): pass");
} else {
System.out.println("test_splitUint8To2bytes(): fail");
}
}
private static void test_combine2bytesToOne() {
byte high = 0x01;
byte low = 0x04;
if (combine2bytesToOne(high, low) == 20) {
System.out.println("test_combine2bytesToOne(): pass");
} else {
System.out.println("test_combine2bytesToOne(): fail");
}
}
private static void test_convertChar2Uint8() {
byte b1 = 'a';
// -128: 1000 0000 should be 128 in unsigned char
// -1: 1111 1111 should be 255 in unsigned char
byte b2 = (byte) -128;
byte b3 = (byte) -1;
if (convertByte2Uint8(b1) == 97 && convertByte2Uint8(b2) == 128
&& convertByte2Uint8(b3) == 255) {
System.out.println("test_convertChar2Uint8(): pass");
} else {
System.out.println("test_convertChar2Uint8(): fail");
}
}
private static void test_convertUint8toByte() {
char c1 = 'a';
// 128: 1000 0000 should be -128 in byte
// 255: 1111 1111 should be -1 in byte
char c2 = 128;
char c3 = 255;
if (convertUint8toByte(c1) == 97 && convertUint8toByte(c2) == -128
&& convertUint8toByte(c3) == -1) {
System.out.println("test_convertUint8toByte(): pass");
} else {
System.out.println("test_convertUint8toByte(): fail");
}
}
private static void test_parseBssid() {
byte b[] = {15, -2, 52, -102, -93, -60};
if (parseBssid(b).equals("0ffe349aa3c4")) {
System.out.println("test_parseBssid(): pass");
} else {
System.out.println("test_parseBssid(): fail");
}
}
public static void main(String args[]) {
test_convertUint8toByte();
test_convertChar2Uint8();
test_splitUint8To2bytes();
test_combine2bytesToOne();
test_parseBssid();
}
}

View File

@@ -0,0 +1,63 @@
package com.espressif.iot.esptouch.util;
import java.util.zip.Checksum;
public class CRC8 implements Checksum {
private static final short[] crcTable = new short[256];
private static final short CRC_POLYNOM = 0x8c;
private static final short CRC_INITIAL = 0x00;
static {
for (int dividend = 0; dividend < 256; dividend++) {
int remainder = dividend;// << 8;
for (int bit = 0; bit < 8; ++bit)
if ((remainder & 0x01) != 0)
remainder = (remainder >>> 1) ^ CRC_POLYNOM;
else
remainder >>>= 1;
crcTable[dividend] = (short) remainder;
}
}
private final short init;
private short value;
public CRC8() {
this.value = this.init = CRC_INITIAL;
}
@Override
public void update(byte[] buffer, int offset, int len) {
for (int i = 0; i < len; i++) {
int data = buffer[offset + i] ^ value;
value = (short) (crcTable[data & 0xff] ^ (value << 8));
}
}
/**
* Updates the current checksum with the specified array of bytes.
* Equivalent to calling <code>update(buffer, 0, buffer.length)</code>.
*
* @param buffer the byte array to update the checksum with
*/
public void update(byte[] buffer) {
update(buffer, 0, buffer.length);
}
@Override
public void update(int b) {
update(new byte[]{(byte) b}, 0, 1);
}
@Override
public long getValue() {
return value & 0xff;
}
@Override
public void reset() {
value = init;
}
}

View File

@@ -0,0 +1,118 @@
package com.espressif.iot.esptouch.util;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class TouchNetUtil {
/**
* get the local ip address by Android System
*
* @param context the context
* @return the local ip addr allocated by Ap
*/
public static InetAddress getLocalInetAddress(Context context) {
WifiManager wm = (WifiManager) context.getApplicationContext()
.getSystemService(Context.WIFI_SERVICE);
assert wm != null;
WifiInfo wifiInfo = wm.getConnectionInfo();
int localAddrInt = wifiInfo.getIpAddress();
String localAddrStr = __formatString(localAddrInt);
InetAddress localInetAddr = null;
try {
localInetAddr = InetAddress.getByName(localAddrStr);
} catch (UnknownHostException e) {
e.printStackTrace();
}
return localInetAddr;
}
private static String __formatString(int value) {
StringBuilder strValue = new StringBuilder();
byte[] ary = __intToByteArray(value);
for (int i = ary.length - 1; i >= 0; i--) {
strValue.append(ary[i] & 0xFF);
if (i > 0) {
strValue.append(".");
}
}
return strValue.toString();
}
private static byte[] __intToByteArray(int value) {
byte[] b = new byte[4];
for (int i = 0; i < 4; i++) {
int offset = (b.length - 1 - i) * 8;
b[i] = (byte) ((value >>> offset) & 0xFF);
}
return b;
}
/**
* parse InetAddress
*
* @param inetAddrBytes
* @return
*/
public static InetAddress parseInetAddr(byte[] inetAddrBytes, int offset,
int count) {
InetAddress inetAddress = null;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) {
sb.append((inetAddrBytes[offset + i] & 0xff));
if (i != count - 1) {
sb.append('.');
}
}
try {
inetAddress = InetAddress.getByName(sb.toString());
} catch (UnknownHostException e) {
e.printStackTrace();
}
return inetAddress;
}
/**
* parse bssid
*
* @param bssid the bssid like aa:bb:cc:dd:ee:ff
* @return byte converted from bssid
*/
public static byte[] parseBssid2bytes(String bssid) {
String[] bssidSplits = bssid.split(":");
byte[] result = new byte[bssidSplits.length];
for (int i = 0; i < bssidSplits.length; i++) {
result[i] = (byte) Integer.parseInt(bssidSplits[i], 16);
}
return result;
}
public static byte[] getOriginalSsidBytes(WifiInfo info) {
try {
Method method = info.getClass().getMethod("getWifiSsid");
method.setAccessible(true);
Object wifiSsid = method.invoke(info);
if (wifiSsid == null) {
return null;
}
method = wifiSsid.getClass().getMethod("getOctets");
method.setAccessible(true);
return (byte[]) method.invoke(wifiSsid);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
return null;
}
}