#88 fixed
+ ArchiveInputStream can be reused with different content types + Reusing input stream between service runs
This commit is contained in:
parent
90c7694b94
commit
2a3eac37c5
|
@ -622,6 +622,7 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
||||||
private boolean mAborted;
|
private boolean mAborted;
|
||||||
|
|
||||||
private DfuCallback mDfuServiceImpl;
|
private DfuCallback mDfuServiceImpl;
|
||||||
|
private InputStream mFirmwareInputStream, mInitFileInputStream;
|
||||||
|
|
||||||
private final BroadcastReceiver mDfuActionReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver mDfuActionReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -879,6 +880,20 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
||||||
unregisterReceiver(mDfuActionReceiver);
|
unregisterReceiver(mDfuActionReceiver);
|
||||||
unregisterReceiver(mConnectionStateBroadcastReceiver);
|
unregisterReceiver(mConnectionStateBroadcastReceiver);
|
||||||
unregisterReceiver(mBondStateBroadcastReceiver);
|
unregisterReceiver(mBondStateBroadcastReceiver);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Ensure that input stream is always closed
|
||||||
|
if (mFirmwareInputStream != null)
|
||||||
|
mFirmwareInputStream.close();
|
||||||
|
if (mInitFileInputStream != null)
|
||||||
|
mInitFileInputStream.close();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
// do nothing
|
||||||
|
} finally {
|
||||||
|
mFirmwareInputStream = null;
|
||||||
|
mInitFileInputStream = null;
|
||||||
|
}
|
||||||
|
logi("DFU service destroyed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -946,12 +961,20 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
||||||
/*
|
/*
|
||||||
* First the service is trying to read the firmware and init packet files.
|
* First the service is trying to read the firmware and init packet files.
|
||||||
*/
|
*/
|
||||||
InputStream is = null;
|
InputStream is = mFirmwareInputStream;
|
||||||
InputStream initIs = null;
|
InputStream initIs = mInitFileInputStream;
|
||||||
int imageSizeInBytes;
|
|
||||||
try {
|
try {
|
||||||
|
final boolean firstRun = mFirmwareInputStream == null;
|
||||||
|
|
||||||
// Prepare data to send, calculate stream size
|
// Prepare data to send, calculate stream size
|
||||||
try {
|
try {
|
||||||
|
if (firstRun) {
|
||||||
|
// The files are opened only once, when DFU service is first started.
|
||||||
|
// In case the service needs to be restarted (for example a buttonless service
|
||||||
|
// was found or to send Application in the second connection) the input stream
|
||||||
|
// is kept as a global service field. This is to avoid SecurityException
|
||||||
|
// when the URI was granted with one-time read permission.
|
||||||
|
// See: Intent#FLAG_GRANT_READ_URI_PERMISSION (https://developer.android.com/reference/android/content/Intent.html#FLAG_GRANT_READ_URI_PERMISSION).
|
||||||
sendLogBroadcast(LOG_LEVEL_VERBOSE, "Opening file...");
|
sendLogBroadcast(LOG_LEVEL_VERBOSE, "Opening file...");
|
||||||
if (fileUri != null) {
|
if (fileUri != null) {
|
||||||
is = openInputStream(fileUri, mimeType, mbrSize, fileType);
|
is = openInputStream(fileUri, mimeType, mbrSize, fileType);
|
||||||
|
@ -961,6 +984,10 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
||||||
is = openInputStream(fileResId, mimeType, mbrSize, fileType);
|
is = openInputStream(fileResId, mimeType, mbrSize, fileType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Init file Input Stream is kept global only in case it was provided
|
||||||
|
// as an argument (separate file for HEX/BIN and DAT files).
|
||||||
|
// If a ZIP file was given with DAT file(s) inside it will be taken from the ZIP
|
||||||
|
// ~20 lines below.
|
||||||
if (initFileUri != null) {
|
if (initFileUri != null) {
|
||||||
// Try to read the Init Packet file from URI
|
// Try to read the Init Packet file from URI
|
||||||
initIs = getContentResolver().openInputStream(initFileUri);
|
initIs = getContentResolver().openInputStream(initFileUri);
|
||||||
|
@ -972,19 +999,19 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
||||||
initIs = getResources().openRawResource(initFileResId);
|
initIs = getResources().openRawResource(initFileResId);
|
||||||
}
|
}
|
||||||
|
|
||||||
imageSizeInBytes = is.available();
|
final int imageSizeInBytes = is.available();
|
||||||
|
|
||||||
if ((imageSizeInBytes % 4) != 0)
|
if ((imageSizeInBytes % 4) != 0)
|
||||||
throw new SizeValidationException("The new firmware is not word-aligned.");
|
throw new SizeValidationException("The new firmware is not word-aligned.");
|
||||||
|
}
|
||||||
|
|
||||||
// Update the file type bit field basing on the ZIP content
|
// Update the file type bit field basing on the ZIP content
|
||||||
if (fileType == TYPE_AUTO && MIME_TYPE_ZIP.equals(mimeType)) {
|
|
||||||
final ArchiveInputStream zhis = (ArchiveInputStream) is;
|
|
||||||
fileType = zhis.getContentType();
|
|
||||||
}
|
|
||||||
// Set the Init packet stream in case of a ZIP file
|
|
||||||
if (MIME_TYPE_ZIP.equals(mimeType)) {
|
if (MIME_TYPE_ZIP.equals(mimeType)) {
|
||||||
final ArchiveInputStream zhis = (ArchiveInputStream) is;
|
final ArchiveInputStream zhis = (ArchiveInputStream) is;
|
||||||
|
if (fileType == TYPE_AUTO) {
|
||||||
|
fileType = zhis.getContentType();
|
||||||
|
} else {
|
||||||
|
fileType = zhis.setContentType(fileType);
|
||||||
|
}
|
||||||
|
|
||||||
// Validate sizes
|
// Validate sizes
|
||||||
if ((fileType & TYPE_APPLICATION) > 0 && (zhis.applicationImageSize() % 4) != 0)
|
if ((fileType & TYPE_APPLICATION) > 0 && (zhis.applicationImageSize() % 4) != 0)
|
||||||
|
@ -1002,7 +1029,9 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
||||||
initIs = new ByteArrayInputStream(zhis.getSystemInit());
|
initIs = new ByteArrayInputStream(zhis.getSystemInit());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendLogBroadcast(LOG_LEVEL_INFO, "Firmware file opened (" + imageSizeInBytes + " bytes in total)");
|
mFirmwareInputStream = is;
|
||||||
|
mInitFileInputStream = initIs;
|
||||||
|
sendLogBroadcast(LOG_LEVEL_INFO, "Firmware file opened successfully");
|
||||||
} catch (final SecurityException e) {
|
} catch (final SecurityException e) {
|
||||||
loge("A security exception occurred while opening file", e);
|
loge("A security exception occurred while opening file", e);
|
||||||
sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: Permission required");
|
sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: Permission required");
|
||||||
|
@ -1030,10 +1059,12 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!firstRun) {
|
||||||
// Wait a second... If we were connected before it's good to give some time before we start reconnecting.
|
// Wait a second... If we were connected before it's good to give some time before we start reconnecting.
|
||||||
waitFor(1000);
|
waitFor(1000);
|
||||||
// Looks like a second is not enough. The ACL_DISCONNECTED broadcast sometimes comes later (on Android 7.0)
|
// Looks like a second is not enough. The ACL_DISCONNECTED broadcast sometimes comes later (on Android 7.0)
|
||||||
waitFor(1000);
|
waitFor(1000);
|
||||||
|
}
|
||||||
|
|
||||||
mProgressInfo = new DfuProgressInfo(this);
|
mProgressInfo = new DfuProgressInfo(this);
|
||||||
|
|
||||||
|
@ -1163,13 +1194,6 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
|
||||||
// Ensure that input stream is always closed
|
|
||||||
if (is != null)
|
|
||||||
is.close();
|
|
||||||
} catch (final IOException e) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
if (foregroundService) {
|
if (foregroundService) {
|
||||||
// This will stop foreground state and, if the progress notifications were disabled
|
// This will stop foreground state and, if the progress notifications were disabled
|
||||||
// it will also remove the notification indicating foreground service.
|
// it will also remove the notification indicating foreground service.
|
||||||
|
|
|
@ -77,6 +77,7 @@ public class ArchiveInputStream extends ZipInputStream {
|
||||||
private byte[] systemInitBytes;
|
private byte[] systemInitBytes;
|
||||||
private byte[] applicationInitBytes;
|
private byte[] applicationInitBytes;
|
||||||
private byte[] currentSource;
|
private byte[] currentSource;
|
||||||
|
private int type;
|
||||||
private int bytesReadFromCurrentSource;
|
private int bytesReadFromCurrentSource;
|
||||||
private int softDeviceSize;
|
private int softDeviceSize;
|
||||||
private int bootloaderSize;
|
private int bootloaderSize;
|
||||||
|
@ -251,6 +252,7 @@ public class ArchiveInputStream extends ZipInputStream {
|
||||||
}
|
}
|
||||||
mark(0);
|
mark(0);
|
||||||
} finally {
|
} finally {
|
||||||
|
type = getContentType();
|
||||||
super.close();
|
super.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -372,9 +374,6 @@ public class ArchiveInputStream extends ZipInputStream {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() throws IOException {
|
public void reset() throws IOException {
|
||||||
if (applicationBytes != null && (softDeviceBytes != null || bootloaderBytes != null || softDeviceAndBootloaderBytes != null))
|
|
||||||
throw new UnsupportedOperationException("Application must be sent in a separate connection.");
|
|
||||||
|
|
||||||
currentSource = markedSource;
|
currentSource = markedSource;
|
||||||
bytesRead = bytesReadFromCurrentSource = bytesReadFromMarkedSource;
|
bytesRead = bytesReadFromCurrentSource = bytesReadFromMarkedSource;
|
||||||
|
|
||||||
|
@ -409,7 +408,7 @@ public class ArchiveInputStream extends ZipInputStream {
|
||||||
* TYPE_APPLICATION}
|
* TYPE_APPLICATION}
|
||||||
*/
|
*/
|
||||||
public int getContentType() {
|
public int getContentType() {
|
||||||
byte type = 0;
|
type = 0;
|
||||||
// In Secure DFU the softDeviceSize and bootloaderSize may be 0 if both are in the ZIP file. The size of each part is embedded in the Init packet.
|
// In Secure DFU the softDeviceSize and bootloaderSize may be 0 if both are in the ZIP file. The size of each part is embedded in the Init packet.
|
||||||
if (softDeviceAndBootloaderBytes != null)
|
if (softDeviceAndBootloaderBytes != null)
|
||||||
type |= DfuBaseService.TYPE_SOFT_DEVICE | DfuBaseService.TYPE_BOOTLOADER;
|
type |= DfuBaseService.TYPE_SOFT_DEVICE | DfuBaseService.TYPE_BOOTLOADER;
|
||||||
|
@ -431,33 +430,40 @@ public class ArchiveInputStream extends ZipInputStream {
|
||||||
* @return the final type after truncating
|
* @return the final type after truncating
|
||||||
*/
|
*/
|
||||||
public int setContentType(final int type) {
|
public int setContentType(final int type) {
|
||||||
if (bytesRead > 0)
|
this.type = type;
|
||||||
throw new UnsupportedOperationException("Content type must not be change after reading content");
|
// If the new type has Application, but there is no application fw, remove this type bit
|
||||||
|
if ((type & DfuBaseService.TYPE_APPLICATION) > 0 && applicationBytes == null)
|
||||||
|
this.type &= ~DfuBaseService.TYPE_APPLICATION;
|
||||||
|
// If the new type has SD+BL
|
||||||
|
if ((type & (DfuBaseService.TYPE_SOFT_DEVICE | DfuBaseService.TYPE_BOOTLOADER)) == (DfuBaseService.TYPE_SOFT_DEVICE | DfuBaseService.TYPE_BOOTLOADER)) {
|
||||||
|
// but there is no SD, remove the softdevice type bit
|
||||||
|
if (softDeviceBytes == null && softDeviceAndBootloaderBytes == null)
|
||||||
|
this.type &= ~DfuBaseService.TYPE_SOFT_DEVICE;
|
||||||
|
// or there is no BL, remove the bootloader type bit
|
||||||
|
if (bootloaderBytes == null && softDeviceAndBootloaderBytes == null)
|
||||||
|
this.type &= ~DfuBaseService.TYPE_SOFT_DEVICE;
|
||||||
|
} else {
|
||||||
|
// If at least one of SD or B: bit is cleared, but the SD+BL file is set, remove both bits.
|
||||||
|
if (softDeviceAndBootloaderBytes != null)
|
||||||
|
this.type &= ~(DfuBaseService.TYPE_SOFT_DEVICE | DfuBaseService.TYPE_BOOTLOADER);
|
||||||
|
}
|
||||||
|
|
||||||
final int t = getContentType() & type;
|
if ((type & (DfuBaseService.TYPE_SOFT_DEVICE | DfuBaseService.TYPE_BOOTLOADER)) > 0 && softDeviceAndBootloaderBytes != null)
|
||||||
|
currentSource = softDeviceAndBootloaderBytes;
|
||||||
if ((t & DfuBaseService.TYPE_SOFT_DEVICE) == 0) {
|
else if ((type & DfuBaseService.TYPE_SOFT_DEVICE) > 0)
|
||||||
softDeviceBytes = null;
|
currentSource = softDeviceBytes;
|
||||||
if (softDeviceAndBootloaderBytes != null) {
|
else if ((type & DfuBaseService.TYPE_BOOTLOADER) > 0)
|
||||||
softDeviceAndBootloaderBytes = null;
|
currentSource = bootloaderBytes;
|
||||||
bootloaderSize = 0;
|
else if ((type & DfuBaseService.TYPE_APPLICATION) > 0)
|
||||||
}
|
currentSource = applicationBytes;
|
||||||
softDeviceSize = 0;
|
bytesReadFromCurrentSource = 0;
|
||||||
}
|
try {
|
||||||
if ((t & DfuBaseService.TYPE_BOOTLOADER) == 0) {
|
|
||||||
bootloaderBytes = null;
|
|
||||||
if (softDeviceAndBootloaderBytes != null) {
|
|
||||||
softDeviceAndBootloaderBytes = null;
|
|
||||||
softDeviceSize = 0;
|
|
||||||
}
|
|
||||||
bootloaderSize = 0;
|
|
||||||
}
|
|
||||||
if ((t & DfuBaseService.TYPE_APPLICATION) == 0) {
|
|
||||||
applicationBytes = null;
|
|
||||||
applicationSize = 0;
|
|
||||||
}
|
|
||||||
mark(0);
|
mark(0);
|
||||||
return t;
|
reset();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
// not available
|
||||||
|
}
|
||||||
|
return this.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -467,9 +473,9 @@ public class ArchiveInputStream extends ZipInputStream {
|
||||||
*/
|
*/
|
||||||
private byte[] startNextFile() {
|
private byte[] startNextFile() {
|
||||||
byte[] ret;
|
byte[] ret;
|
||||||
if (currentSource == softDeviceBytes && bootloaderBytes != null) {
|
if (currentSource == softDeviceBytes && bootloaderBytes != null && (type & DfuBaseService.TYPE_BOOTLOADER) > 0) {
|
||||||
ret = currentSource = bootloaderBytes;
|
ret = currentSource = bootloaderBytes;
|
||||||
} else if (currentSource != applicationBytes && applicationBytes != null) {
|
} else if (currentSource != applicationBytes && applicationBytes != null && (type & DfuBaseService.TYPE_APPLICATION) > 0) {
|
||||||
ret = currentSource = applicationBytes;
|
ret = currentSource = applicationBytes;
|
||||||
} else {
|
} else {
|
||||||
ret = currentSource = null;
|
ret = currentSource = null;
|
||||||
|
@ -488,11 +494,12 @@ public class ArchiveInputStream extends ZipInputStream {
|
||||||
// This method then is just used to log file size.
|
// This method then is just used to log file size.
|
||||||
|
|
||||||
// In case of SD+BL in Secure DFU:
|
// In case of SD+BL in Secure DFU:
|
||||||
if (softDeviceAndBootloaderBytes != null && softDeviceSize == 0 && bootloaderSize == 0)
|
if (softDeviceAndBootloaderBytes != null && softDeviceSize == 0 && bootloaderSize == 0
|
||||||
return softDeviceAndBootloaderBytes.length + applicationSize - bytesRead;
|
&& (type & (DfuBaseService.TYPE_SOFT_DEVICE | DfuBaseService.TYPE_BOOTLOADER)) > 0)
|
||||||
|
return softDeviceAndBootloaderBytes.length + applicationImageSize() - bytesRead;
|
||||||
|
|
||||||
// Otherwise:
|
// Otherwise:
|
||||||
return softDeviceSize + bootloaderSize + applicationSize - bytesRead;
|
return softDeviceImageSize() + bootloaderImageSize() + applicationImageSize() - bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -500,7 +507,7 @@ public class ArchiveInputStream extends ZipInputStream {
|
||||||
* @return the size of the SoftDevice firmware (BIN part)
|
* @return the size of the SoftDevice firmware (BIN part)
|
||||||
*/
|
*/
|
||||||
public int softDeviceImageSize() {
|
public int softDeviceImageSize() {
|
||||||
return softDeviceSize;
|
return (type & DfuBaseService.TYPE_SOFT_DEVICE) > 0 ? softDeviceSize : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -508,7 +515,7 @@ public class ArchiveInputStream extends ZipInputStream {
|
||||||
* @return the size of the Bootloader firmware (BIN part)
|
* @return the size of the Bootloader firmware (BIN part)
|
||||||
*/
|
*/
|
||||||
public int bootloaderImageSize() {
|
public int bootloaderImageSize() {
|
||||||
return bootloaderSize;
|
return (type & DfuBaseService.TYPE_BOOTLOADER) > 0 ? bootloaderSize : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -516,7 +523,7 @@ public class ArchiveInputStream extends ZipInputStream {
|
||||||
* @return the size of the Application firmware (BIN part)
|
* @return the size of the Application firmware (BIN part)
|
||||||
*/
|
*/
|
||||||
public int applicationImageSize() {
|
public int applicationImageSize() {
|
||||||
return applicationSize;
|
return (type & DfuBaseService.TYPE_APPLICATION) > 0 ? applicationSize : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue