#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 DfuCallback mDfuServiceImpl;
|
||||
private InputStream mFirmwareInputStream, mInitFileInputStream;
|
||||
|
||||
private final BroadcastReceiver mDfuActionReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
|
@ -879,6 +880,20 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
|||
unregisterReceiver(mDfuActionReceiver);
|
||||
unregisterReceiver(mConnectionStateBroadcastReceiver);
|
||||
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
|
||||
|
@ -946,53 +961,65 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
|||
/*
|
||||
* First the service is trying to read the firmware and init packet files.
|
||||
*/
|
||||
InputStream is = null;
|
||||
InputStream initIs = null;
|
||||
int imageSizeInBytes;
|
||||
InputStream is = mFirmwareInputStream;
|
||||
InputStream initIs = mInitFileInputStream;
|
||||
try {
|
||||
final boolean firstRun = mFirmwareInputStream == null;
|
||||
|
||||
// Prepare data to send, calculate stream size
|
||||
try {
|
||||
sendLogBroadcast(LOG_LEVEL_VERBOSE, "Opening file...");
|
||||
if (fileUri != null) {
|
||||
is = openInputStream(fileUri, mimeType, mbrSize, fileType);
|
||||
} else if (filePath != null) {
|
||||
is = openInputStream(filePath, mimeType, mbrSize, fileType);
|
||||
} else if (fileResId > 0) {
|
||||
is = openInputStream(fileResId, mimeType, mbrSize, fileType);
|
||||
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...");
|
||||
if (fileUri != null) {
|
||||
is = openInputStream(fileUri, mimeType, mbrSize, fileType);
|
||||
} else if (filePath != null) {
|
||||
is = openInputStream(filePath, mimeType, mbrSize, fileType);
|
||||
} else if (fileResId > 0) {
|
||||
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) {
|
||||
// Try to read the Init Packet file from URI
|
||||
initIs = getContentResolver().openInputStream(initFileUri);
|
||||
} else if (initFilePath != null) {
|
||||
// Try to read the Init Packet file from path
|
||||
initIs = new FileInputStream(initFilePath);
|
||||
} else if (initFileResId > 0) {
|
||||
// Try to read the Init Packet file from given resource
|
||||
initIs = getResources().openRawResource(initFileResId);
|
||||
}
|
||||
|
||||
final int imageSizeInBytes = is.available();
|
||||
if ((imageSizeInBytes % 4) != 0)
|
||||
throw new SizeValidationException("The new firmware is not word-aligned.");
|
||||
}
|
||||
|
||||
if (initFileUri != null) {
|
||||
// Try to read the Init Packet file from URI
|
||||
initIs = getContentResolver().openInputStream(initFileUri);
|
||||
} else if (initFilePath != null) {
|
||||
// Try to read the Init Packet file from path
|
||||
initIs = new FileInputStream(initFilePath);
|
||||
} else if (initFileResId > 0) {
|
||||
// Try to read the Init Packet file from given resource
|
||||
initIs = getResources().openRawResource(initFileResId);
|
||||
}
|
||||
|
||||
imageSizeInBytes = is.available();
|
||||
|
||||
if ((imageSizeInBytes % 4) != 0)
|
||||
throw new SizeValidationException("The new firmware is not word-aligned.");
|
||||
|
||||
// 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)) {
|
||||
final ArchiveInputStream zhis = (ArchiveInputStream) is;
|
||||
if (fileType == TYPE_AUTO) {
|
||||
fileType = zhis.getContentType();
|
||||
} else {
|
||||
fileType = zhis.setContentType(fileType);
|
||||
}
|
||||
|
||||
// Validate sizes
|
||||
if ((fileType & TYPE_APPLICATION) > 0 && (zhis.applicationImageSize() % 4) != 0)
|
||||
throw new SizeValidationException("Application firmware is not word-aligned.");
|
||||
if ((fileType & TYPE_BOOTLOADER) > 0 && (zhis.bootloaderImageSize() % 4) != 0)
|
||||
throw new SizeValidationException("Bootloader firmware is not word-aligned.");
|
||||
if ((fileType & TYPE_SOFT_DEVICE) > 0 && (zhis.softDeviceImageSize() % 4) != 0)
|
||||
throw new SizeValidationException("Soft Device firmware is not word-aligned.");
|
||||
// Validate sizes
|
||||
if ((fileType & TYPE_APPLICATION) > 0 && (zhis.applicationImageSize() % 4) != 0)
|
||||
throw new SizeValidationException("Application firmware is not word-aligned.");
|
||||
if ((fileType & TYPE_BOOTLOADER) > 0 && (zhis.bootloaderImageSize() % 4) != 0)
|
||||
throw new SizeValidationException("Bootloader firmware is not word-aligned.");
|
||||
if ((fileType & TYPE_SOFT_DEVICE) > 0 && (zhis.softDeviceImageSize() % 4) != 0)
|
||||
throw new SizeValidationException("Soft Device firmware is not word-aligned.");
|
||||
|
||||
if (fileType == TYPE_APPLICATION) {
|
||||
if (zhis.getApplicationInit() != null)
|
||||
|
@ -1002,7 +1029,9 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
|||
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) {
|
||||
loge("A security exception occurred while opening file", e);
|
||||
sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: Permission required");
|
||||
|
@ -1013,11 +1042,11 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
|||
sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: File not found");
|
||||
report(ERROR_FILE_NOT_FOUND);
|
||||
return;
|
||||
} catch (final SizeValidationException e) {
|
||||
loge("Firmware not word-aligned", e);
|
||||
} catch (final SizeValidationException e) {
|
||||
loge("Firmware not word-aligned", e);
|
||||
sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: Firmware size must be word-aligned");
|
||||
report(ERROR_FILE_SIZE_INVALID);
|
||||
return;
|
||||
report(ERROR_FILE_SIZE_INVALID);
|
||||
return;
|
||||
} catch (final IOException e) {
|
||||
loge("An exception occurred while calculating file size", e);
|
||||
sendLogBroadcast(LOG_LEVEL_ERROR, "Opening file failed: " + e.getLocalizedMessage());
|
||||
|
@ -1030,10 +1059,12 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
|||
return;
|
||||
}
|
||||
|
||||
// Wait a second... If we were connected before it's good to give some time before we start reconnecting.
|
||||
waitFor(1000);
|
||||
// Looks like a second is not enough. The ACL_DISCONNECTED broadcast sometimes comes later (on Android 7.0)
|
||||
waitFor(1000);
|
||||
if (!firstRun) {
|
||||
// Wait a second... If we were connected before it's good to give some time before we start reconnecting.
|
||||
waitFor(1000);
|
||||
// Looks like a second is not enough. The ACL_DISCONNECTED broadcast sometimes comes later (on Android 7.0)
|
||||
waitFor(1000);
|
||||
}
|
||||
|
||||
mProgressInfo = new DfuProgressInfo(this);
|
||||
|
||||
|
@ -1163,13 +1194,6 @@ public abstract class DfuBaseService extends IntentService implements DfuProgres
|
|||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
// Ensure that input stream is always closed
|
||||
if (is != null)
|
||||
is.close();
|
||||
} catch (final IOException e) {
|
||||
// do nothing
|
||||
}
|
||||
if (foregroundService) {
|
||||
// This will stop foreground state and, if the progress notifications were disabled
|
||||
// it will also remove the notification indicating foreground service.
|
||||
|
|
|
@ -77,6 +77,7 @@ public class ArchiveInputStream extends ZipInputStream {
|
|||
private byte[] systemInitBytes;
|
||||
private byte[] applicationInitBytes;
|
||||
private byte[] currentSource;
|
||||
private int type;
|
||||
private int bytesReadFromCurrentSource;
|
||||
private int softDeviceSize;
|
||||
private int bootloaderSize;
|
||||
|
@ -251,6 +252,7 @@ public class ArchiveInputStream extends ZipInputStream {
|
|||
}
|
||||
mark(0);
|
||||
} finally {
|
||||
type = getContentType();
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
@ -372,9 +374,6 @@ public class ArchiveInputStream extends ZipInputStream {
|
|||
|
||||
@Override
|
||||
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;
|
||||
bytesRead = bytesReadFromCurrentSource = bytesReadFromMarkedSource;
|
||||
|
||||
|
@ -409,7 +408,7 @@ public class ArchiveInputStream extends ZipInputStream {
|
|||
* TYPE_APPLICATION}
|
||||
*/
|
||||
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.
|
||||
if (softDeviceAndBootloaderBytes != null)
|
||||
type |= DfuBaseService.TYPE_SOFT_DEVICE | DfuBaseService.TYPE_BOOTLOADER;
|
||||
|
@ -431,33 +430,40 @@ public class ArchiveInputStream extends ZipInputStream {
|
|||
* @return the final type after truncating
|
||||
*/
|
||||
public int setContentType(final int type) {
|
||||
if (bytesRead > 0)
|
||||
throw new UnsupportedOperationException("Content type must not be change after reading content");
|
||||
this.type = type;
|
||||
// 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 ((t & DfuBaseService.TYPE_SOFT_DEVICE) == 0) {
|
||||
softDeviceBytes = null;
|
||||
if (softDeviceAndBootloaderBytes != null) {
|
||||
softDeviceAndBootloaderBytes = null;
|
||||
bootloaderSize = 0;
|
||||
}
|
||||
softDeviceSize = 0;
|
||||
if ((type & (DfuBaseService.TYPE_SOFT_DEVICE | DfuBaseService.TYPE_BOOTLOADER)) > 0 && softDeviceAndBootloaderBytes != null)
|
||||
currentSource = softDeviceAndBootloaderBytes;
|
||||
else if ((type & DfuBaseService.TYPE_SOFT_DEVICE) > 0)
|
||||
currentSource = softDeviceBytes;
|
||||
else if ((type & DfuBaseService.TYPE_BOOTLOADER) > 0)
|
||||
currentSource = bootloaderBytes;
|
||||
else if ((type & DfuBaseService.TYPE_APPLICATION) > 0)
|
||||
currentSource = applicationBytes;
|
||||
bytesReadFromCurrentSource = 0;
|
||||
try {
|
||||
mark(0);
|
||||
reset();
|
||||
} catch (final IOException e) {
|
||||
// not available
|
||||
}
|
||||
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);
|
||||
return t;
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -467,9 +473,9 @@ public class ArchiveInputStream extends ZipInputStream {
|
|||
*/
|
||||
private byte[] startNextFile() {
|
||||
byte[] ret;
|
||||
if (currentSource == softDeviceBytes && bootloaderBytes != null) {
|
||||
if (currentSource == softDeviceBytes && bootloaderBytes != null && (type & DfuBaseService.TYPE_BOOTLOADER) > 0) {
|
||||
ret = currentSource = bootloaderBytes;
|
||||
} else if (currentSource != applicationBytes && applicationBytes != null) {
|
||||
} else if (currentSource != applicationBytes && applicationBytes != null && (type & DfuBaseService.TYPE_APPLICATION) > 0) {
|
||||
ret = currentSource = applicationBytes;
|
||||
} else {
|
||||
ret = currentSource = null;
|
||||
|
@ -488,11 +494,12 @@ public class ArchiveInputStream extends ZipInputStream {
|
|||
// This method then is just used to log file size.
|
||||
|
||||
// In case of SD+BL in Secure DFU:
|
||||
if (softDeviceAndBootloaderBytes != null && softDeviceSize == 0 && bootloaderSize == 0)
|
||||
return softDeviceAndBootloaderBytes.length + applicationSize - bytesRead;
|
||||
if (softDeviceAndBootloaderBytes != null && softDeviceSize == 0 && bootloaderSize == 0
|
||||
&& (type & (DfuBaseService.TYPE_SOFT_DEVICE | DfuBaseService.TYPE_BOOTLOADER)) > 0)
|
||||
return softDeviceAndBootloaderBytes.length + applicationImageSize() - bytesRead;
|
||||
|
||||
// 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)
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
public int applicationImageSize() {
|
||||
return applicationSize;
|
||||
return (type & DfuBaseService.TYPE_APPLICATION) > 0 ? applicationSize : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue