Loading core/java/android/os/RedactingFileDescriptor.java +21 −5 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ import java.util.Arrays; /** * Variant of {@link FileDescriptor} that allows its creator to specify regions * that should be redacted (appearing as zeros to the reader). * that should be redacted. * * @hide */ Loading @@ -44,13 +44,16 @@ public class RedactingFileDescriptor { private static final boolean DEBUG = true; private volatile long[] mRedactRanges; private volatile long[] mFreeOffsets; private FileDescriptor mInner = null; private ParcelFileDescriptor mOuter = null; private RedactingFileDescriptor(Context context, File file, int mode, long[] redactRanges) private RedactingFileDescriptor( Context context, File file, int mode, long[] redactRanges, long[] freeOffsets) throws IOException { mRedactRanges = checkRangesArgument(redactRanges); mFreeOffsets = freeOffsets; try { try { Loading Loading @@ -88,13 +91,17 @@ public class RedactingFileDescriptor { * * @param file The underlying file to open. * @param mode The {@link ParcelFileDescriptor} mode to open with. * @param redactRanges List of file offsets that should be redacted, stored * @param redactRanges List of file ranges that should be redacted, stored * as {@code [start1, end1, start2, end2, ...]}. Start values are * inclusive and end values are exclusive. * @param freePositions List of file offsets at which the four byte value 'free' should be * written instead of zeros within parts of the file covered by {@code redactRanges}. * Non-redacted bytes will not be modified even if covered by a 'free'. This is * useful for overwriting boxes in ISOBMFF files with padding data. */ public static ParcelFileDescriptor open(Context context, File file, int mode, long[] redactRanges) throws IOException { return new RedactingFileDescriptor(context, file, mode, redactRanges).mOuter; long[] redactRanges, long[] freePositions) throws IOException { return new RedactingFileDescriptor(context, file, mode, redactRanges, freePositions).mOuter; } /** Loading Loading @@ -169,6 +176,15 @@ public class RedactingFileDescriptor { for (long j = start; j < end; j++) { data[(int) (j - offset)] = 0; } // Overwrite data at 'free' offsets within the redaction ranges. for (long freeOffset : mFreeOffsets) { final long freeEnd = freeOffset + 4; final long redactFreeStart = Math.max(freeOffset, start); final long redactFreeEnd = Math.min(freeEnd, end); for (long j = redactFreeStart; j < redactFreeEnd; j++) { data[(int) (j - offset)] = (byte) "free".charAt((int) (j - freeOffset)); } } } return n; } Loading core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java +76 −4 Original line number Diff line number Diff line Loading @@ -64,7 +64,7 @@ public class RedactingFileDescriptorTest { @Test public void testSingleByte() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY, new long[] { 10, 11 }).getFileDescriptor(); new long[] { 10, 11 }, new long[] {}).getFileDescriptor(); final byte[] buf = new byte[1_000]; assertEquals(buf.length, Os.read(fd, buf, 0, buf.length)); Loading @@ -80,7 +80,7 @@ public class RedactingFileDescriptorTest { @Test public void testRanges() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY, new long[] { 100, 200, 300, 400 }).getFileDescriptor(); new long[] { 100, 200, 300, 400 }, new long[] {}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 90)); Loading @@ -102,7 +102,7 @@ public class RedactingFileDescriptorTest { @Test public void testEntireFile() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY, new long[] { 0, 5_000_000 }).getFileDescriptor(); new long[] { 0, 5_000_000 }, new long[] {}).getFileDescriptor(); try (FileInputStream in = new FileInputStream(fd)) { int val; Loading @@ -115,7 +115,7 @@ public class RedactingFileDescriptorTest { @Test public void testReadWrite() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 100, 200, 300, 400 }).getFileDescriptor(); new long[] { 100, 200, 300, 400 }, new long[] {}).getFileDescriptor(); // Redacted at first final byte[] buf = new byte[10]; Loading Loading @@ -168,4 +168,76 @@ public class RedactingFileDescriptorTest { assertArrayEquals(new long[] { 100, 200 }, removeRange(new long[] { 100, 200 }, 150, 150)); } @Test public void testFreeAtStart() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 10 }, new long[] {1}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, (byte) 'f', (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0, 0, 0 }, buf); } @Test public void testFreeAtOffset() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 10 }, new long[] {3}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, 0, 0, (byte) 'f', (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0 }, buf); } @Test public void testFreeAcrossRedactionStart() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 10 }, new long[] {0}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0, 0, 0, 0 }, buf); } @Test public void testFreeAcrossRedactionEnd() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 3 }, new long[] {2}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, 0, (byte) 'f', 64, 64, 64, 64, 64, 64, 64 }, buf); } @Test public void testFreeOutsideRedaction() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 8 }, new long[] { 8 }).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, 0, 0, 0, 0, 0, 0, 0, 64, 64 }, buf); } @Test public void testFreeMultipleRedactions() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 2, 3, 4 }, new long[] { 0 }).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, (byte) 'r', 64, (byte) 'e', 64, 64, 64, 64, 64, 64 }, buf); } } Loading
core/java/android/os/RedactingFileDescriptor.java +21 −5 Original line number Diff line number Diff line Loading @@ -35,7 +35,7 @@ import java.util.Arrays; /** * Variant of {@link FileDescriptor} that allows its creator to specify regions * that should be redacted (appearing as zeros to the reader). * that should be redacted. * * @hide */ Loading @@ -44,13 +44,16 @@ public class RedactingFileDescriptor { private static final boolean DEBUG = true; private volatile long[] mRedactRanges; private volatile long[] mFreeOffsets; private FileDescriptor mInner = null; private ParcelFileDescriptor mOuter = null; private RedactingFileDescriptor(Context context, File file, int mode, long[] redactRanges) private RedactingFileDescriptor( Context context, File file, int mode, long[] redactRanges, long[] freeOffsets) throws IOException { mRedactRanges = checkRangesArgument(redactRanges); mFreeOffsets = freeOffsets; try { try { Loading Loading @@ -88,13 +91,17 @@ public class RedactingFileDescriptor { * * @param file The underlying file to open. * @param mode The {@link ParcelFileDescriptor} mode to open with. * @param redactRanges List of file offsets that should be redacted, stored * @param redactRanges List of file ranges that should be redacted, stored * as {@code [start1, end1, start2, end2, ...]}. Start values are * inclusive and end values are exclusive. * @param freePositions List of file offsets at which the four byte value 'free' should be * written instead of zeros within parts of the file covered by {@code redactRanges}. * Non-redacted bytes will not be modified even if covered by a 'free'. This is * useful for overwriting boxes in ISOBMFF files with padding data. */ public static ParcelFileDescriptor open(Context context, File file, int mode, long[] redactRanges) throws IOException { return new RedactingFileDescriptor(context, file, mode, redactRanges).mOuter; long[] redactRanges, long[] freePositions) throws IOException { return new RedactingFileDescriptor(context, file, mode, redactRanges, freePositions).mOuter; } /** Loading Loading @@ -169,6 +176,15 @@ public class RedactingFileDescriptor { for (long j = start; j < end; j++) { data[(int) (j - offset)] = 0; } // Overwrite data at 'free' offsets within the redaction ranges. for (long freeOffset : mFreeOffsets) { final long freeEnd = freeOffset + 4; final long redactFreeStart = Math.max(freeOffset, start); final long redactFreeEnd = Math.min(freeEnd, end); for (long j = redactFreeStart; j < redactFreeEnd; j++) { data[(int) (j - offset)] = (byte) "free".charAt((int) (j - freeOffset)); } } } return n; } Loading
core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java +76 −4 Original line number Diff line number Diff line Loading @@ -64,7 +64,7 @@ public class RedactingFileDescriptorTest { @Test public void testSingleByte() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY, new long[] { 10, 11 }).getFileDescriptor(); new long[] { 10, 11 }, new long[] {}).getFileDescriptor(); final byte[] buf = new byte[1_000]; assertEquals(buf.length, Os.read(fd, buf, 0, buf.length)); Loading @@ -80,7 +80,7 @@ public class RedactingFileDescriptorTest { @Test public void testRanges() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY, new long[] { 100, 200, 300, 400 }).getFileDescriptor(); new long[] { 100, 200, 300, 400 }, new long[] {}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 90)); Loading @@ -102,7 +102,7 @@ public class RedactingFileDescriptorTest { @Test public void testEntireFile() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY, new long[] { 0, 5_000_000 }).getFileDescriptor(); new long[] { 0, 5_000_000 }, new long[] {}).getFileDescriptor(); try (FileInputStream in = new FileInputStream(fd)) { int val; Loading @@ -115,7 +115,7 @@ public class RedactingFileDescriptorTest { @Test public void testReadWrite() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 100, 200, 300, 400 }).getFileDescriptor(); new long[] { 100, 200, 300, 400 }, new long[] {}).getFileDescriptor(); // Redacted at first final byte[] buf = new byte[10]; Loading Loading @@ -168,4 +168,76 @@ public class RedactingFileDescriptorTest { assertArrayEquals(new long[] { 100, 200 }, removeRange(new long[] { 100, 200 }, 150, 150)); } @Test public void testFreeAtStart() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 10 }, new long[] {1}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, (byte) 'f', (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0, 0, 0 }, buf); } @Test public void testFreeAtOffset() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 10 }, new long[] {3}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, 0, 0, (byte) 'f', (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0 }, buf); } @Test public void testFreeAcrossRedactionStart() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 10 }, new long[] {0}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, (byte) 'r', (byte) 'e', (byte) 'e', 0, 0, 0, 0, 0, 0 }, buf); } @Test public void testFreeAcrossRedactionEnd() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 3 }, new long[] {2}).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, 0, (byte) 'f', 64, 64, 64, 64, 64, 64, 64 }, buf); } @Test public void testFreeOutsideRedaction() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 8 }, new long[] { 8 }).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, 0, 0, 0, 0, 0, 0, 0, 64, 64 }, buf); } @Test public void testFreeMultipleRedactions() throws Exception { final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE, new long[] { 1, 2, 3, 4 }, new long[] { 0 }).getFileDescriptor(); final byte[] buf = new byte[10]; assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 0)); assertArrayEquals( new byte[] { 64, (byte) 'r', 64, (byte) 'e', 64, 64, 64, 64, 64, 64 }, buf); } }