Annotations added, warnings removed
This commit is contained in:
parent
a37e5328d7
commit
ac585ac926
|
@ -22,6 +22,8 @@
|
||||||
|
|
||||||
package no.nordicsemi.android.dfu.internal;
|
package no.nordicsemi.android.dfu.internal;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.FilterInputStream;
|
import java.io.FilterInputStream;
|
||||||
|
@ -31,13 +33,15 @@ import java.io.InputStream;
|
||||||
import no.nordicsemi.android.dfu.internal.exception.HexFileValidationException;
|
import no.nordicsemi.android.dfu.internal.exception.HexFileValidationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the binary content from the HEX file using IntelHex standard: http://www.interlog.com/~speff/usefulinfo/Hexfrmt.pdf.
|
* Reads the binary content from the HEX file using IntelHex standard:
|
||||||
|
* http://www.interlog.com/~speff/usefulinfo/Hexfrmt.pdf.
|
||||||
* Truncates the HEX file from all meta data and returns only the BIN content.
|
* Truncates the HEX file from all meta data and returns only the BIN content.
|
||||||
* <p>
|
* <p>
|
||||||
* In nRF51 chips memory a SoftDevice starts at address 0x1000. From 0x0000 to 0x1000 there is MBR sector (since SoftDevice 7.0.0) which should not be transmitted using DFU. Therefore this class skips
|
* In nRF51 chips memory a SoftDevice starts at address 0x1000. From 0x0000 to 0x1000 there is
|
||||||
* all data from addresses below 0x1000.
|
* MBR sector (since SoftDevice 7.0.0) which should not be transmitted using DFU. Therefore this
|
||||||
* </p>
|
* class skips all data from addresses below 0x1000.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||||
public class HexInputStream extends FilterInputStream {
|
public class HexInputStream extends FilterInputStream {
|
||||||
private final int LINE_LENGTH = 128;
|
private final int LINE_LENGTH = 128;
|
||||||
|
|
||||||
|
@ -50,18 +54,18 @@ public class HexInputStream extends FilterInputStream {
|
||||||
private final int MBRSize;
|
private final int MBRSize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the HEX Input Stream. The constructor calculates the size of the BIN content which is available through {@link #sizeInBytes()}. If HEX file is invalid then the bin size is 0.
|
* Creates the HEX Input Stream. The constructor calculates the size of the BIN content which
|
||||||
|
* is available through {@link #sizeInBytes()}. If HEX file is invalid then the bin size is 0.
|
||||||
*
|
*
|
||||||
* @param in
|
* @param in the input stream to read from.
|
||||||
* the input stream to read from
|
* @param mbrSize the MBR (Master Boot Record) size in bytes. Data with addresses below than
|
||||||
* @param mbrSize
|
* number will be trimmed and not transferred to DFU target.
|
||||||
* The MBR (Master Boot Record) size in bytes. Data with addresses below than number will be trimmed and not transferred to DFU target.
|
* @throws HexFileValidationException if HEX file is invalid, e.g. there is no semicolon (':')
|
||||||
* @throws HexFileValidationException
|
* on the beginning of each line.
|
||||||
* if HEX file is invalid. F.e. there is no semicolon (':') on the beginning of each line.
|
* @throws IOException if the stream is closed or another IOException occurs.
|
||||||
* @throws java.io.IOException
|
|
||||||
* if the stream is closed or another IOException occurs.
|
|
||||||
*/
|
*/
|
||||||
public HexInputStream(final InputStream in, final int mbrSize) throws HexFileValidationException, IOException {
|
public HexInputStream(@NonNull final InputStream in, final int mbrSize)
|
||||||
|
throws HexFileValidationException, IOException {
|
||||||
super(new BufferedInputStream(in));
|
super(new BufferedInputStream(in));
|
||||||
this.localBuf = new byte[LINE_LENGTH];
|
this.localBuf = new byte[LINE_LENGTH];
|
||||||
this.localPos = LINE_LENGTH; // we are at the end of the local buffer, new one must be obtained
|
this.localPos = LINE_LENGTH; // we are at the end of the local buffer, new one must be obtained
|
||||||
|
@ -72,7 +76,19 @@ public class HexInputStream extends FilterInputStream {
|
||||||
this.available = calculateBinSize(mbrSize);
|
this.available = calculateBinSize(mbrSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HexInputStream(final byte[] data, final int mbrSize) throws HexFileValidationException, IOException {
|
/**
|
||||||
|
* Creates the HEX Input Stream. The constructor calculates the size of the BIN content which
|
||||||
|
* is available through {@link #sizeInBytes()}. If HEX file is invalid then the bin size is 0.
|
||||||
|
*
|
||||||
|
* @param data the input stream to read from.
|
||||||
|
* @param mbrSize the MBR (Master Boot Record) size in bytes. Data with addresses below than
|
||||||
|
* * number will be trimmed and not transferred to DFU target.
|
||||||
|
* @throws HexFileValidationException if HEX file is invalid, e.g. there is no semicolon (':')
|
||||||
|
* on the beginning of each line.
|
||||||
|
* @throws IOException if the stream is closed or another IOException occurs.
|
||||||
|
*/
|
||||||
|
public HexInputStream(@NonNull final byte[] data, final int mbrSize)
|
||||||
|
throws HexFileValidationException, IOException {
|
||||||
super(new ByteArrayInputStream(data));
|
super(new ByteArrayInputStream(data));
|
||||||
this.localBuf = new byte[LINE_LENGTH];
|
this.localBuf = new byte[LINE_LENGTH];
|
||||||
this.localPos = LINE_LENGTH; // we are at the end of the local buffer, new one must be obtained
|
this.localPos = LINE_LENGTH; // we are at the end of the local buffer, new one must be obtained
|
||||||
|
@ -83,6 +99,7 @@ public class HexInputStream extends FilterInputStream {
|
||||||
this.available = calculateBinSize(mbrSize);
|
this.available = calculateBinSize(mbrSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
private int calculateBinSize(final int mbrSize) throws IOException {
|
private int calculateBinSize(final int mbrSize) throws IOException {
|
||||||
int binSize = 0;
|
int binSize = 0;
|
||||||
final InputStream in = this.in;
|
final InputStream in = this.in;
|
||||||
|
@ -100,50 +117,52 @@ public class HexInputStream extends FilterInputStream {
|
||||||
offset = readAddress(in);// reading the offset
|
offset = readAddress(in);// reading the offset
|
||||||
type = readByte(in); // reading the line type
|
type = readByte(in); // reading the line type
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0x01:
|
case 0x01:
|
||||||
// end of file
|
// end of file
|
||||||
return binSize;
|
|
||||||
case 0x04: {
|
|
||||||
// extended linear address record
|
|
||||||
/*
|
|
||||||
* The HEX file may contain jump to different addresses. The MSB of LBA (Linear Base Address) is given using the line type 4.
|
|
||||||
* We only support files where bytes are located together, no jumps are allowed. Therefore the newULBA may be only lastULBA + 1 (or any, if this is the first line of the HEX)
|
|
||||||
*/
|
|
||||||
final int newULBA = readAddress(in);
|
|
||||||
if (binSize > 0 && newULBA != (lastBaseAddress >> 16) + 1)
|
|
||||||
return binSize;
|
return binSize;
|
||||||
lastBaseAddress = newULBA << 16;
|
case 0x04: {
|
||||||
skip(in, 2 /* check sum */);
|
// extended linear address record
|
||||||
break;
|
/*
|
||||||
}
|
* The HEX file may contain jump to different addresses.
|
||||||
case 0x02: {
|
* The MSB of LBA (Linear Base Address) is given using the line type 4.
|
||||||
// extended segment address record
|
* We only support files where bytes are located together, no jumps are
|
||||||
final int newSBA = readAddress(in) << 4;
|
* allowed. Therefore the newULBA may be only lastULBA + 1 (or any,
|
||||||
if (binSize > 0 && (newSBA >> 16) != (lastBaseAddress >> 16) + 1)
|
* if this is the first line of the HEX)
|
||||||
return binSize;
|
*/
|
||||||
lastBaseAddress = newSBA;
|
final int newULBA = readAddress(in);
|
||||||
skip(in, 2 /* check sum */);
|
if (binSize > 0 && newULBA != (lastBaseAddress >> 16) + 1)
|
||||||
break;
|
return binSize;
|
||||||
}
|
lastBaseAddress = newULBA << 16;
|
||||||
case 0x00:
|
skip(in, 2 /* check sum */);
|
||||||
// data type line
|
|
||||||
lastAddress = lastBaseAddress + offset;
|
|
||||||
if (lastAddress >= mbrSize) // we must skip all data from below last MBR address (default 0x1000) as those are the MBR. The Soft Device starts at the end of MBR (0x1000), the app and bootloader farther more
|
|
||||||
binSize += lineSize;
|
|
||||||
// no break!
|
|
||||||
default:
|
|
||||||
final long toBeSkipped = lineSize * 2 /* 2 hex per one byte */+ 2 /* check sum */;
|
|
||||||
skip(in, toBeSkipped);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// skip end of line
|
|
||||||
while (true) {
|
|
||||||
b = in.read();
|
|
||||||
|
|
||||||
if (b != '\n' && b != '\r') {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 0x02: {
|
||||||
|
// extended segment address record
|
||||||
|
final int newSBA = readAddress(in) << 4;
|
||||||
|
if (binSize > 0 && (newSBA >> 16) != (lastBaseAddress >> 16) + 1)
|
||||||
|
return binSize;
|
||||||
|
lastBaseAddress = newSBA;
|
||||||
|
skip(in, 2 /* check sum */);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x00:
|
||||||
|
// data type line
|
||||||
|
lastAddress = lastBaseAddress + offset;
|
||||||
|
// we must skip all data from below last MBR address (default 0x1000)
|
||||||
|
// as those are the MBR. The Soft Device starts at the end of MBR (0x1000),
|
||||||
|
// the app and bootloader farther more
|
||||||
|
if (lastAddress >= mbrSize)
|
||||||
|
binSize += lineSize;
|
||||||
|
// no break!
|
||||||
|
default:
|
||||||
|
final long toBeSkipped = lineSize * 2 /* 2 hex per one byte */ + 2 /* check sum */;
|
||||||
|
skip(in, toBeSkipped);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
// skip end of line
|
||||||
|
do {
|
||||||
|
b = in.read();
|
||||||
|
} while (b == '\n' || b == '\r');
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
in.reset();
|
in.reset();
|
||||||
|
@ -160,9 +179,8 @@ public class HexInputStream extends FilterInputStream {
|
||||||
*
|
*
|
||||||
* @param buffer buffer to be filled
|
* @param buffer buffer to be filled
|
||||||
* @return the size of the buffer
|
* @return the size of the buffer
|
||||||
* @throws java.io.IOException
|
|
||||||
*/
|
*/
|
||||||
public int readPacket(byte[] buffer) throws HexFileValidationException, IOException {
|
public int readPacket(@NonNull byte[] buffer) throws IOException {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < buffer.length) {
|
while (i < buffer.length) {
|
||||||
if (localPos < size) {
|
if (localPos < size) {
|
||||||
|
@ -178,17 +196,17 @@ public class HexInputStream extends FilterInputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException {
|
public int read() {
|
||||||
throw new UnsupportedOperationException("Please, use readPacket() method instead");
|
throw new UnsupportedOperationException("Please, use readPacket() method instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buffer) throws IOException {
|
public int read(@NonNull byte[] buffer) throws IOException {
|
||||||
return readPacket(buffer);
|
return readPacket(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buffer, int offset, int count) throws IOException {
|
public int read(@NonNull byte[] buffer, int offset, int count) {
|
||||||
throw new UnsupportedOperationException("Please, use readPacket() method instead");
|
throw new UnsupportedOperationException("Please, use readPacket() method instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,25 +220,24 @@ public class HexInputStream extends FilterInputStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total number of packets with given size that are needed to get all available data
|
* Returns the total number of packets with given size that are needed to get all
|
||||||
|
* available data.
|
||||||
*
|
*
|
||||||
* @param packetSize
|
* @param packetSize the maximum packet size
|
||||||
* the maximum packet size
|
|
||||||
* @return the number of packets needed to get all the content
|
* @return the number of packets needed to get all the content
|
||||||
* @throws java.io.IOException
|
|
||||||
*/
|
*/
|
||||||
public int sizeInPackets(final int packetSize) throws IOException {
|
public int sizeInPackets(final int packetSize) {
|
||||||
final int sizeInBytes = sizeInBytes();
|
final int sizeInBytes = sizeInBytes();
|
||||||
|
|
||||||
return sizeInBytes / packetSize + ((sizeInBytes % packetSize) > 0 ? 1 : 0);
|
return sizeInBytes / packetSize + ((sizeInBytes % packetSize) > 0 ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads new line from the input stream. Input stream must be a HEX file. The first line is always skipped.
|
* Reads new line from the input stream. Input stream must be a HEX file.
|
||||||
|
* The first line is always skipped.
|
||||||
*
|
*
|
||||||
* @return the number of data bytes in the new line. 0 if end of file.
|
* @return the number of data bytes in the new line. 0 if end of file.
|
||||||
* @throws java.io.IOException
|
* @throws java.io.IOException if this stream is closed or another IOException occurs.
|
||||||
* if this stream is closed or another IOException occurs.
|
|
||||||
*/
|
*/
|
||||||
private int readLine() throws IOException {
|
private int readLine() throws IOException {
|
||||||
// end of file reached
|
// end of file reached
|
||||||
|
@ -234,23 +251,21 @@ public class HexInputStream extends FilterInputStream {
|
||||||
int lineSize, type, offset;
|
int lineSize, type, offset;
|
||||||
do {
|
do {
|
||||||
// skip end of line
|
// skip end of line
|
||||||
while (true) {
|
do {
|
||||||
b = in.read();
|
b = in.read();
|
||||||
pos++;
|
pos++;
|
||||||
|
} while (b == '\n' || b == '\r');
|
||||||
if (b != '\n' && b != '\r') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each line starts with comma (':')
|
* Each line starts with comma (':')
|
||||||
* Data is written in HEX, so each 2 ASCII letters give one byte.
|
* Data is written in HEX, so each 2 ASCII letters give one byte.
|
||||||
* After the comma there is one byte (2 HEX signs) with line length (normally 10 -> 0x10 -> 16 bytes -> 32 HEX characters)
|
* After the comma there is one byte (2 HEX signs) with line length
|
||||||
|
* (normally 10 -> 0x10 -> 16 bytes -> 32 HEX characters)
|
||||||
* After that there is a 4 byte of an address. This part may be skipped.
|
* After that there is a 4 byte of an address. This part may be skipped.
|
||||||
* There is a packet type after the address (1 byte = 2 HEX characters). 00 is the valid data. Other values can be skipped when
|
* There is a packet type after the address (1 byte = 2 HEX characters).
|
||||||
* converting to BIN file.
|
* 00 is the valid data. Other values can be skipped when converting to BIN file.
|
||||||
* Then goes n bytes of data followed by 1 byte (2 HEX chars) of checksum, which is also skipped in BIN file.
|
* Then goes n bytes of data followed by 1 byte (2 HEX chars) of checksum,
|
||||||
|
* which is also skipped in BIN file.
|
||||||
*/
|
*/
|
||||||
checkComma(b); // checking the comma at the beginning
|
checkComma(b); // checking the comma at the beginning
|
||||||
lineSize = readByte(in); // reading the length of the data in this line
|
lineSize = readByte(in); // reading the length of the data in this line
|
||||||
|
@ -262,41 +277,41 @@ public class HexInputStream extends FilterInputStream {
|
||||||
|
|
||||||
// if the line type is no longer data type (0x00), we've reached the end of the file
|
// if the line type is no longer data type (0x00), we've reached the end of the file
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
// data type
|
// data type
|
||||||
if (lastAddress + offset < MBRSize) { // skip MBR
|
if (lastAddress + offset < MBRSize) { // skip MBR
|
||||||
type = -1; // some other than 0
|
type = -1; // some other than 0
|
||||||
pos += skip(in, lineSize * 2 /* 2 hex per one byte */+ 2 /* check sum */);
|
pos += skip(in, lineSize * 2 /* 2 hex per one byte */ + 2 /* check sum */);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x01:
|
||||||
|
// end of file
|
||||||
|
pos = -1;
|
||||||
|
return 0;
|
||||||
|
case 0x02: {
|
||||||
|
// extended segment address
|
||||||
|
final int address = readAddress(in) << 4;
|
||||||
|
pos += 4;
|
||||||
|
if (bytesRead > 0 && (address >> 16) != (lastAddress >> 16) + 1)
|
||||||
|
return 0;
|
||||||
|
lastAddress = address;
|
||||||
|
pos += skip(in, 2 /* check sum */);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
case 0x04: {
|
||||||
case 0x01:
|
// extended linear address
|
||||||
// end of file
|
final int address = readAddress(in);
|
||||||
pos = -1;
|
pos += 4;
|
||||||
return 0;
|
if (bytesRead > 0 && address != (lastAddress >> 16) + 1)
|
||||||
case 0x02: {
|
return 0;
|
||||||
// extended segment address
|
lastAddress = address << 16;
|
||||||
final int address = readAddress(in) << 4;
|
pos += skip(in, 2 /* check sum */);
|
||||||
pos += 4;
|
break;
|
||||||
if (bytesRead > 0 && (address >> 16) != (lastAddress >> 16) + 1)
|
}
|
||||||
return 0;
|
default:
|
||||||
lastAddress = address;
|
final long toBeSkipped = lineSize * 2 /* 2 hex per one byte */ + 2 /* check sum */;
|
||||||
pos += skip(in, 2 /* check sum */);
|
pos += skip(in, toBeSkipped);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case 0x04: {
|
|
||||||
// extended linear address
|
|
||||||
final int address = readAddress(in);
|
|
||||||
pos += 4;
|
|
||||||
if (bytesRead > 0 && address != (lastAddress >> 16) + 1)
|
|
||||||
return 0;
|
|
||||||
lastAddress = address << 16;
|
|
||||||
pos += skip(in, 2 /* check sum */);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
final long toBeSkipped = lineSize * 2 /* 2 hex per one byte */+ 2 /* check sum */;
|
|
||||||
pos += skip(in, toBeSkipped);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} while (type != 0);
|
} while (type != 0);
|
||||||
|
|
||||||
|
@ -326,22 +341,23 @@ public class HexInputStream extends FilterInputStream {
|
||||||
throw new HexFileValidationException("Not a HEX file");
|
throw new HexFileValidationException("Not a HEX file");
|
||||||
}
|
}
|
||||||
|
|
||||||
private long skip(final InputStream in, final long offset) throws IOException {
|
private long skip(@NonNull final InputStream in, final long offset) throws IOException {
|
||||||
long skipped = in.skip(offset);
|
long skipped = in.skip(offset);
|
||||||
// try to skip 2 times as skip(..) method does not guarantee to skip exactly given number of bytes
|
// try to skip 2 times as skip(..) method does not guarantee to skip exactly
|
||||||
|
// given number of bytes
|
||||||
if (skipped < offset)
|
if (skipped < offset)
|
||||||
skipped += in.skip(offset - skipped);
|
skipped += in.skip(offset - skipped);
|
||||||
return skipped;
|
return skipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readByte(final InputStream in) throws IOException {
|
private int readByte(@NonNull final InputStream in) throws IOException {
|
||||||
final int first = asciiToInt(in.read());
|
final int first = asciiToInt(in.read());
|
||||||
final int second = asciiToInt(in.read());
|
final int second = asciiToInt(in.read());
|
||||||
|
|
||||||
return first << 4 | second;
|
return first << 4 | second;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readAddress(final InputStream in) throws IOException {
|
private int readAddress(@NonNull final InputStream in) throws IOException {
|
||||||
return readByte(in) << 8 | readByte(in);
|
return readByte(in) << 8 | readByte(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue