more SAF implementation

* full support for Directory API (Android Lollipop or later)
* best effort to handle any kind errors (missing file, revoked permissions, etc) and recover the download
* implemented directory choosing
* fix download database version upgrading
* misc. cleanup
* do not release permission on the old save path (if the user change the download directory) under SAF api
This commit is contained in:
kapodamy 2019-04-09 18:38:34 -03:00
parent f6b32823ba
commit d00dc798f4
28 changed files with 946 additions and 589 deletions

View file

@ -12,7 +12,7 @@ public class CircularFileWriter extends SharpStream {
private final static int QUEUE_BUFFER_SIZE = 8 * 1024;// 8 KiB
private final static int NOTIFY_BYTES_INTERVAL = 64 * 1024;// 64 KiB
private final static int THRESHOLD_AUX_LENGTH = 3 * 1024 * 1024;// 3 MiB
private final static int THRESHOLD_AUX_LENGTH = 15 * 1024 * 1024;// 15 MiB
private OffsetChecker callback;
@ -44,41 +44,84 @@ public class CircularFileWriter extends SharpStream {
reportPosition = NOTIFY_BYTES_INTERVAL;
}
private void flushAuxiliar() throws IOException {
private void flushAuxiliar(long amount) throws IOException {
if (aux.length < 1) {
return;
}
boolean underflow = out.getOffset() >= out.length;
out.flush();
aux.flush();
boolean underflow = aux.offset < aux.length || out.offset < out.length;
aux.target.seek(0);
out.target.seek(out.length);
long length = aux.length;
out.length += aux.length;
long length = amount;
while (length > 0) {
int read = (int) Math.min(length, Integer.MAX_VALUE);
read = aux.target.read(aux.queue, 0, Math.min(read, aux.queue.length));
if (read < 1) {
amount -= length;
break;
}
out.writeProof(aux.queue, read);
length -= read;
}
if (underflow) {
out.offset += aux.offset;
out.target.seek(out.offset);
if (out.offset >= out.length) {
// calculate the aux underflow pointer
if (aux.offset < amount) {
out.offset += aux.offset;
aux.offset = 0;
out.target.seek(out.offset);
} else {
aux.offset -= amount;
out.offset = out.length + amount;
}
} else {
aux.offset = 0;
}
} else {
out.offset = out.length;
out.offset += amount;
aux.offset -= amount;
}
out.length += amount;
if (out.length > maxLengthKnown) {
maxLengthKnown = out.length;
}
if (amount < aux.length) {
// move the excess data to the beginning of the file
long readOffset = amount;
long writeOffset = 0;
byte[] buffer = new byte[128 * 1024]; // 128 KiB
aux.length -= amount;
length = aux.length;
while (length > 0) {
int read = (int) Math.min(length, Integer.MAX_VALUE);
read = aux.target.read(buffer, 0, Math.min(read, buffer.length));
aux.target.seek(writeOffset);
aux.writeProof(buffer, read);
writeOffset += read;
readOffset += read;
length -= read;
aux.target.seek(readOffset);
}
aux.target.setLength(aux.length);
return;
}
if (aux.length > THRESHOLD_AUX_LENGTH) {
aux.target.setLength(THRESHOLD_AUX_LENGTH);// or setLength(0);
}
@ -94,7 +137,7 @@ public class CircularFileWriter extends SharpStream {
* @throws IOException if an I/O error occurs
*/
public long finalizeFile() throws IOException {
flushAuxiliar();
flushAuxiliar(aux.length);
out.flush();
@ -148,7 +191,7 @@ public class CircularFileWriter extends SharpStream {
if (end == -1) {
available = Integer.MAX_VALUE;
} else if (end < offsetOut) {
throw new IOException("The reported offset is invalid: " + String.valueOf(offsetOut));
throw new IOException("The reported offset is invalid: " + end + "<" + offsetOut);
} else {
available = end - offsetOut;
}
@ -167,16 +210,10 @@ public class CircularFileWriter extends SharpStream {
length = aux.length + len;
}
if (length > available || length < THRESHOLD_AUX_LENGTH) {
aux.write(b, off, len);
} else {
if (underflow) {
aux.write(b, off, len);
flushAuxiliar();
} else {
flushAuxiliar();
out.write(b, off, len);// write directly on the output
}
aux.write(b, off, len);
if (length >= THRESHOLD_AUX_LENGTH && length <= available) {
flushAuxiliar(available);
}
} else {
if (underflow) {
@ -234,8 +271,13 @@ public class CircularFileWriter extends SharpStream {
@Override
public void seek(long offset) throws IOException {
long total = out.length + aux.length;
if (offset == total) {
return;// nothing to do
// do not ignore the seek offset if a underflow exists
long relativeOffset = out.getOffset() + aux.getOffset();
if (relativeOffset == total) {
return;
}
}
// flush everything, avoid any underflow
@ -409,6 +451,9 @@ public class CircularFileWriter extends SharpStream {
}
protected void seek(long absoluteOffset) throws IOException {
if (absoluteOffset == offset) {
return;// nothing to do
}
offset = absoluteOffset;
target.seek(absoluteOffset);
}