blob: 2edd419fd4ef6fad535bc05680e8ac8acd9c9c1b [file] [log] [blame]
package org.robolectric.shadows;
import static android.os.Build.VERSION_CODES.KITKAT;
import static org.robolectric.shadow.api.Shadow.invokeConstructor;
import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
import android.annotation.SuppressLint;
import android.os.ParcelFileDescriptor;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Constructor;
import java.util.UUID;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
@Implements(ParcelFileDescriptor.class)
@SuppressLint("NewApi")
public class ShadowParcelFileDescriptor {
// TODO: consider removing this shadow in favor of shadowing file operations at the libcore.os
// level
private static final String PIPE_TMP_DIR = "ShadowParcelFileDescriptor";
private static final String PIPE_FILE_NAME = "pipe";
private RandomAccessFile file;
@RealObject ParcelFileDescriptor realParcelFd;
private @RealObject ParcelFileDescriptor realObject;
@Implementation
protected void __constructor__(ParcelFileDescriptor wrapped) {
invokeConstructor(
ParcelFileDescriptor.class, realObject, from(ParcelFileDescriptor.class, wrapped));
if (wrapped != null) {
ShadowParcelFileDescriptor shadowParcelFileDescriptor = Shadow.extract(wrapped);
this.file = shadowParcelFileDescriptor.file;
}
}
@Implementation
protected static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
ParcelFileDescriptor pfd;
try {
Constructor<ParcelFileDescriptor> constructor =
ParcelFileDescriptor.class.getDeclaredConstructor(FileDescriptor.class);
pfd = constructor.newInstance(new FileDescriptor());
} catch (Exception e) {
throw new RuntimeException(e);
}
ShadowParcelFileDescriptor shadowParcelFileDescriptor = Shadow.extract(pfd);
shadowParcelFileDescriptor.file = new RandomAccessFile(file, getFileMode(mode));
if ((mode & ParcelFileDescriptor.MODE_TRUNCATE) != 0) {
try {
shadowParcelFileDescriptor.file.setLength(0);
} catch (IOException ioe) {
FileNotFoundException fnfe = new FileNotFoundException("Unable to truncate");
fnfe.initCause(ioe);
throw fnfe;
}
}
if ((mode & ParcelFileDescriptor.MODE_APPEND) != 0) {
try {
shadowParcelFileDescriptor.file.seek(shadowParcelFileDescriptor.file.length());
} catch (IOException ioe) {
FileNotFoundException fnfe = new FileNotFoundException("Unable to append");
fnfe.initCause(ioe);
throw fnfe;
}
}
return pfd;
}
private static String getFileMode(int mode) {
if ((mode & ParcelFileDescriptor.MODE_CREATE) != 0) {
return "rw";
}
switch (mode & ParcelFileDescriptor.MODE_READ_WRITE) {
case ParcelFileDescriptor.MODE_READ_ONLY:
return "r";
case ParcelFileDescriptor.MODE_WRITE_ONLY:
case ParcelFileDescriptor.MODE_READ_WRITE:
return "rw";
}
return "rw";
}
@Implementation
protected static ParcelFileDescriptor[] createPipe() throws IOException {
File file =
new File(
RuntimeEnvironment.getTempDirectory().createIfNotExists(PIPE_TMP_DIR).toFile(),
PIPE_FILE_NAME + "-" + UUID.randomUUID());
if (!file.createNewFile()) {
throw new IOException("Cannot create pipe file: " + file.getAbsolutePath());
}
ParcelFileDescriptor readSide = open(file, ParcelFileDescriptor.MODE_READ_ONLY);
ParcelFileDescriptor writeSide = open(file, ParcelFileDescriptor.MODE_READ_WRITE);
file.deleteOnExit();
return new ParcelFileDescriptor[] {readSide, writeSide};
}
@Implementation(minSdk = KITKAT)
protected static ParcelFileDescriptor[] createReliablePipe() throws IOException {
return createPipe();
}
@Implementation
protected FileDescriptor getFileDescriptor() {
try {
return file.getFD();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Implementation
protected long getStatSize() {
try {
return file.length();
} catch (IOException e) {
// This might occur when the file object has been closed.
return -1;
}
}
@Implementation
protected int getFd() {
try {
return ReflectionHelpers.getField(file.getFD(), "fd");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Implementation
protected void close() throws IOException {
file.close();
Shadow.directlyOn(realObject, ParcelFileDescriptor.class).close();
}
}