Fixed bug with Android's CameraRoll module where saveToPhotos crashed devices on a very small subset of devices

Summary:
For a small subset of Android devices, the following line of code caused the device to crash

```
output.transferFrom(input, 0, Long.MAX_VALUE);
```

According to the [Java docs](https://docs.oracle.com/javase/1.5.0/docs/api/java/nio/channels/FileChannel.html), this is what the function does.

> Transfers bytes into this channel's file from the given readable byte channel.
An attempt is made to read up to count bytes from the source channel and write them to this channel's file starting at the given position. An invocation of this method may or may not transfer all of the requested bytes; whether or not it does so depends upon the natures and states of the channels. Fewer than the requested number of bytes will be transferred if the source channel has fewer than count bytes remaining, or if the source channel is non-blocking and has fewer than count bytes immediately available in its input buffer.

Hence, using `Long.MAX_VALUE` seemed to be the standard way to transfer all bytes from one channel to the other.

However, it appeared that for some reason on a subset of old Android devices, the device tries to allocate `count` bytes of memory before transferring the bytes. Obviously, this caused a crash because no device has that much memory. This was fixed transferring bytes using a 1MB buffer.

Differential Revision: D14921778

fbshipit-source-id: 7fa46e10c656e23ae7d5679c72b278188f09ad0a
This commit is contained in:
Alston Lin
2019-04-15 12:29:49 -07:00
committed by Facebook Github Bot
parent 0bd931e47e
commit e28b6f314a

View File

@@ -43,6 +43,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
@@ -111,6 +112,7 @@ public class CameraRollManager extends ReactContextBaseJavaModule {
private static class SaveToCameraRoll extends GuardedAsyncTask<Void, Void> {
private static final int SAVE_BUFFER_SIZE = 1048576; // 1MB
private final Context mContext;
private final Uri mUri;
private final Promise mPromise;
@@ -155,7 +157,18 @@ public class CameraRollManager extends ReactContextBaseJavaModule {
dest = new File(exportDir, sourceName + "_" + (n++) + sourceExt);
}
output = new FileOutputStream(dest).getChannel();
output.transferFrom(input, 0, Long.MAX_VALUE);
// Performs a buffered copy
final ByteBuffer buffer = ByteBuffer.allocate(SAVE_BUFFER_SIZE);
while (input.read(buffer) > 0) {
buffer.flip();
output.write(buffer);
buffer.compact();
}
// Drains the buffer
buffer.flip();
while (buffer.hasRemaining()){
output.write(buffer);
}
input.close();
output.close();