1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.content.res.cts;
18 
19 import static android.system.OsConstants.S_ISFIFO;
20 
21 import static junit.framework.TestCase.assertEquals;
22 import static junit.framework.TestCase.assertFalse;
23 import static junit.framework.TestCase.assertTrue;
24 import static junit.framework.TestCase.fail;
25 
26 import android.content.Context;
27 import android.content.res.AssetFileDescriptor;
28 import android.os.ParcelFileDescriptor;
29 import android.platform.test.annotations.AppModeSdkSandbox;
30 import android.system.Os;
31 import android.system.StructStat;
32 import android.test.MoreAsserts;
33 
34 import androidx.test.ext.junit.runners.AndroidJUnit4;
35 import androidx.test.platform.app.InstrumentationRegistry;
36 
37 import org.junit.After;
38 import org.junit.Before;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 
42 import java.io.BufferedOutputStream;
43 import java.io.File;
44 import java.io.FileDescriptor;
45 import java.io.FileInputStream;
46 import java.io.FileOutputStream;
47 import java.io.IOException;
48 import java.io.OutputStream;
49 import java.nio.ByteBuffer;
50 import java.nio.MappedByteBuffer;
51 import java.nio.channels.FileChannel;
52 import java.nio.channels.WritableByteChannel;
53 
54 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
55 @RunWith(AndroidJUnit4.class)
56 public class AssetFileDescriptor_AutoCloseInputStreamTest {
getContext()57     private Context getContext() {
58         return InstrumentationRegistry.getInstrumentation().getTargetContext();
59     }
60 
61     private static final int FILE_END = -1;
62     private static final String FILE_NAME = "testAssertFileDescriptorAutoCloseInputStream";
63     private static final byte[] FILE_DATA = new byte[]{
64             0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
65     };
66     private static final int FILE_LENGTH = FILE_DATA.length;
67     private static final String METHOD_NOT_SUPPORTED_MESSAGE =
68             "This Method is not supported in AutoCloseInputStream FileChannel.";
69     private File mFile;
70     private AssetFileDescriptor mFd;
71     private AssetFileDescriptor.AutoCloseInputStream mInput;
72 
73     @Before
setUp()74     public void setUp() throws Exception {
75         mFile = new File(getContext().getFilesDir(), FILE_NAME);
76         FileOutputStream outputStream = new FileOutputStream(mFile);
77         outputStream.write(FILE_DATA);
78         outputStream.close();
79     }
80 
81     @After
tearDown()82     public void tearDown() throws Exception {
83         if (mFd != null) {
84             mFd.close();
85             mFd = null;
86         }
87         mFile.delete();
88     }
89 
90     @Test
testSkip()91     public void testSkip() throws IOException {
92         openInput(0, FILE_LENGTH);
93         assertEquals(FILE_DATA[0], mInput.read());
94         assertEquals(0, mInput.skip(0));
95         assertEquals(FILE_DATA[1], mInput.read());
96         assertEquals(3, mInput.skip(3));
97         assertEquals(FILE_DATA[5], mInput.read());
98         assertEquals(3, mInput.skip(10));
99         assertEquals(FILE_END, mInput.read());
100     }
101 
102     @Test
testRead()103     public void testRead() throws IOException {
104         openInput(0, FILE_LENGTH);
105         for (int i = 0; i < FILE_LENGTH; i++) {
106             assertEquals(FILE_DATA[i], mInput.read());
107         }
108         assertEquals(FILE_END, mInput.read());
109     }
110 
111     @Test
testReadPartial()112     public void testReadPartial() throws IOException {
113         long len = 6;
114         openInput(0, len);
115         for (int i = 0; i < len; i++) {
116             assertEquals(FILE_DATA[i], mInput.read());
117         }
118         assertEquals(FILE_END, mInput.read());
119     }
120 
121     @Test
testReadBufferLen()122     public void testReadBufferLen() throws IOException {
123         openInput(0, FILE_LENGTH);
124         byte[] buf = new byte[FILE_LENGTH];
125         assertEquals(3, mInput.read(buf, 0, 3));
126         assertEquals(3, mInput.read(buf, 3, 3));
127         assertEquals(3, mInput.read(buf, 6, 4));
128         MoreAsserts.assertEquals(FILE_DATA, buf);
129         assertEquals(FILE_END, mInput.read(buf, 0, 4));
130     }
131 
132     @Test
testReadBuffer()133     public void testReadBuffer() throws IOException {
134         openInput(0, FILE_LENGTH);
135         byte[] buf = new byte[6];
136         assertEquals(6, mInput.read(buf));
137         assertEquals(FILE_DATA[0], buf[0]);
138         assertEquals(3, mInput.read(buf));
139         assertEquals(FILE_DATA[6], buf[0]);
140         assertEquals(FILE_END, mInput.read(buf));
141     }
142 
143     @Test
testReadBufferPartial()144     public void testReadBufferPartial() throws IOException {
145         long len = 8;
146         openInput(0, len);
147         byte[] buf = new byte[6];
148         assertEquals(6, mInput.read(buf));
149         assertEquals(FILE_DATA[0], buf[0]);
150         assertEquals(2, mInput.read(buf));
151         assertEquals(FILE_DATA[6], buf[0]);
152         assertEquals(FILE_END, mInput.read(buf));
153     }
154 
155     @Test
testAvailableRead()156     public void testAvailableRead() throws IOException {
157         openInput(0, FILE_LENGTH);
158         assertEquals(FILE_LENGTH, mInput.available());
159         assertEquals(FILE_DATA[0], mInput.read());
160         assertEquals(FILE_LENGTH - 1, mInput.available());
161     }
162 
163     @Test
testAvailableReadBuffer()164     public void testAvailableReadBuffer() throws IOException {
165         openInput(0, FILE_LENGTH);
166         byte[] buf = new byte[3];
167         assertEquals(FILE_LENGTH, mInput.available());
168         assertEquals(buf.length, mInput.read(buf));
169         assertEquals(FILE_LENGTH - buf.length, mInput.available());
170     }
171 
172     @Test
testAvailableReadBufferLen()173     public void testAvailableReadBufferLen() throws IOException {
174         openInput(0, FILE_LENGTH);
175         byte[] buf = new byte[3];
176         assertEquals(FILE_LENGTH, mInput.available());
177         assertEquals(2, mInput.read(buf, 0, 2));
178         assertEquals(FILE_LENGTH - 2, mInput.available());
179     }
180 
181     /*
182      * Tests that AutoInputStream doesn't support mark().
183      */
184     @Test
testMark()185     public void testMark() throws IOException {
186         openInput(0, FILE_LENGTH);
187         assertFalse(mInput.markSupported());
188         assertEquals(FILE_DATA[0], mInput.read());
189         mInput.mark(FILE_LENGTH);  // should do nothing
190         assertEquals(FILE_DATA[1], mInput.read());
191         mInput.reset();  // should do nothing
192         assertEquals(FILE_DATA[2], mInput.read());
193     }
194 
195     @Test
testTwoFileDescriptorsWorkIndependently()196     public void testTwoFileDescriptorsWorkIndependently() throws IOException {
197         openInput(0, FILE_LENGTH);
198 
199         AssetFileDescriptor fd2 = new AssetFileDescriptor(mFd.getParcelFileDescriptor(),
200                 0,
201                 FILE_LENGTH);
202         AssetFileDescriptor.AutoCloseInputStream input2 =
203                 new AssetFileDescriptor.AutoCloseInputStream(fd2);
204 
205         input2.skip(2);
206         input2.read();
207 
208         for (int i = 0; i < FILE_LENGTH; i++) {
209             assertEquals(FILE_DATA[i], mInput.read());
210         }
211         assertEquals(FILE_END, mInput.read());
212     }
213 
214     @Test
testReadZeroByte()215     public void testReadZeroByte() throws IOException {
216         openInput(0, FILE_LENGTH);
217         byte[] buf = new byte[1];
218         assertEquals(FILE_LENGTH, mInput.available());
219         assertEquals(0, mInput.read(buf, 0, 0));
220         assertEquals(FILE_LENGTH, mInput.available());
221     }
222 
223     @Test
testReadLargeBytes()224     public void testReadLargeBytes() throws IOException {
225         final int writeSize = 1_000_000;
226         mFile = new File(getContext().getFilesDir(), FILE_NAME);
227         try (OutputStream out =
228                      new BufferedOutputStream(new FileOutputStream(mFile))) {
229             for (int i = 0; i < writeSize; i++) {
230                 out.write(i % 256);
231             }
232         }
233 
234         openInput(0, writeSize);
235 
236         assertEquals(writeSize, mFd.getLength());
237         assertEquals(writeSize, mInput.readAllBytes().length);
238     }
239 
openInput(long startOffset, long length)240     private void openInput(long startOffset, long length)
241             throws IOException {
242         if (mFd != null) {
243             mFd.close();
244             mFd = null;
245         }
246         ParcelFileDescriptor fd =
247                 ParcelFileDescriptor.open(mFile, ParcelFileDescriptor.MODE_READ_WRITE);
248         mFd = new AssetFileDescriptor(fd, startOffset, length);
249         mInput = new AssetFileDescriptor.AutoCloseInputStream(mFd);
250     }
251 
252     /*
253      * Tests that AutoInputStream is returning customized File Channel of getChannel(),
254      * which could help update the channel position.
255      */
256     @Test
testGetChannel()257     public void testGetChannel() throws IOException {
258         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
259         input.skip(2);
260         FileChannel fc = input.getChannel();
261         input.read();
262         assertEquals(3, fc.position());
263     }
264 
265     @Test
testOffsetCorrectFileChannelSize()266     public void testOffsetCorrectFileChannelSize() throws IOException {
267         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
268         FileChannel fc = input.getChannel();
269         assertEquals(fc.size(), FILE_LENGTH);
270     }
271 
272     @Test
testOffsetCorrectFileChannelReadBuffer()273     public void testOffsetCorrectFileChannelReadBuffer() throws IOException {
274         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
275         FileChannel fc = input.getChannel();
276 
277         int startPosition = 0;
278         int bufferSize = 4;
279         ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
280         int bytesRead = fc.read(buffer);
281         assertEquals(bufferSize, bytesRead);
282 
283         buffer.flip();
284         for (int i = startPosition; i < startPosition + bufferSize; i++) {
285             assertEquals(FILE_DATA[i], buffer.get());
286         }
287         assertFalse(buffer.hasRemaining());
288         assertEquals(startPosition + bufferSize, fc.position());
289     }
290 
291     @Test
testOffsetCorrectFileChannelReadBuffers()292     public void testOffsetCorrectFileChannelReadBuffers() throws IOException {
293         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
294         FileChannel fc = input.getChannel();
295 
296         int startPosition = 0;
297         int bufferSize = 4;
298         ByteBuffer[] buffers = new ByteBuffer[1];
299         buffers[0] = ByteBuffer.allocate(bufferSize);
300         fc.read(buffers, 0, buffers.length);
301         buffers[0].flip();
302         for (int i = startPosition; i < startPosition + bufferSize; i++) {
303             assertEquals(FILE_DATA[i], buffers[0].get());
304         }
305         assertFalse(buffers[0].hasRemaining());
306         assertEquals(startPosition + bufferSize, fc.position());
307     }
308 
309     @Test
testOffsetCorrectFileChannelReadBufferFromPosition()310     public void testOffsetCorrectFileChannelReadBufferFromPosition() throws IOException {
311         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
312         FileChannel fc = input.getChannel();
313 
314         int startPosition = 0;
315         int bufferSize = 4;
316         ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
317         int readPosition = 1;
318         fc.read(buffer, readPosition);
319         buffer.flip();
320         for (int i = readPosition; i < readPosition + bufferSize; i++) {
321             assertEquals(FILE_DATA[i], buffer.get());
322         }
323         assertEquals(startPosition, fc.position());
324     }
325 
326     @Test
testOffsetCorrectFileChannelTransferTo()327     public void testOffsetCorrectFileChannelTransferTo() throws IOException {
328         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
329         FileChannel fc = input.getChannel();
330 
331         String outputFileName = "outputFile.txt";
332         File outputFile = new File(getContext().getFilesDir(), outputFileName);
333         FileOutputStream output = new FileOutputStream(outputFile);
334         WritableByteChannel targetChannel = output.getChannel();
335         int startPosition = 1;
336         int transferSize = 3;
337         long bytesTransferred = fc.transferTo(startPosition, transferSize, targetChannel);
338         assertEquals(transferSize, bytesTransferred);
339         assertEquals(0, fc.position());
340 
341         ParcelFileDescriptor fd =
342                 ParcelFileDescriptor.open(outputFile, ParcelFileDescriptor.MODE_READ_WRITE);
343         AssetFileDescriptor afd = new AssetFileDescriptor(fd, 0, transferSize);
344         AssetFileDescriptor.AutoCloseInputStream input2 =
345                 new AssetFileDescriptor.AutoCloseInputStream(afd);
346 
347         for (int i = startPosition; i < startPosition + transferSize; i++) {
348             assertEquals(FILE_DATA[i], input2.read());
349         }
350         assertEquals(-1, input2.read());
351 
352         targetChannel.close();
353         output.close();
354     }
355 
356     @Test
testOffsetCorrectFileChannelMap()357     public void testOffsetCorrectFileChannelMap() throws IOException {
358         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
359         FileChannel fc = input.getChannel();
360 
361         int startPosition = 0;
362         int mapPosition = 1;
363         int mapSize = 4;
364         MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, mapPosition, mapSize);
365         for (int i = mapPosition; i < mapPosition + mapSize; i++) {
366             assertEquals(FILE_DATA[i], mbb.get());
367         }
368         assertFalse(mbb.hasRemaining());
369         assertEquals(startPosition, fc.position());
370     }
371 
372     @Test
testOffsetCorrectFileChannelWriteBuffer()373     public void testOffsetCorrectFileChannelWriteBuffer() throws IOException {
374         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
375         FileChannel fc = input.getChannel();
376 
377         ByteBuffer buffer = ByteBuffer.allocate(1);
378         try {
379             fc.write(buffer);
380             fail();
381         } catch (UnsupportedOperationException e) {
382             assertEquals(METHOD_NOT_SUPPORTED_MESSAGE, e.getMessage());
383         }
384     }
385 
386     @Test
testOffsetCorrectFileChannelWriteBuffers()387     public void testOffsetCorrectFileChannelWriteBuffers() throws IOException {
388         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
389         FileChannel fc = input.getChannel();
390 
391         int bufferSize = 4;
392         ByteBuffer[] buffers = new ByteBuffer[1];
393         buffers[0] = ByteBuffer.allocate(bufferSize);
394         try {
395             fc.write(buffers, 0, 1);
396             fail();
397         } catch (UnsupportedOperationException e) {
398             assertEquals(METHOD_NOT_SUPPORTED_MESSAGE, e.getMessage());
399         }
400     }
401 
402     @Test
testOffsetCorrectFileChannelWriteBufferFromPosition()403     public void testOffsetCorrectFileChannelWriteBufferFromPosition() throws IOException {
404         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
405         FileChannel fc = input.getChannel();
406 
407         int bufferSize = 4;
408         ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
409         try {
410             fc.write(buffer, 0);
411             fail();
412         } catch (UnsupportedOperationException e) {
413             assertEquals(METHOD_NOT_SUPPORTED_MESSAGE, e.getMessage());
414         }
415     }
416 
417     @Test
testOffsetCorrectFileChannelTransferFrom()418     public void testOffsetCorrectFileChannelTransferFrom() throws IOException {
419         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
420         FileChannel fc = input.getChannel();
421 
422         FileInputStream input2 = new FileInputStream(mFile);
423         FileChannel fc2 = input2.getChannel();
424         try {
425             fc.transferFrom(fc2, 0, fc2.size());
426             fail();
427         } catch (UnsupportedOperationException e) {
428             assertEquals(METHOD_NOT_SUPPORTED_MESSAGE, e.getMessage());
429         }
430         fc2.close();
431         input2.close();
432     }
433 
434     @Test
testOffsetCorrectFileChannelTruncate()435     public void testOffsetCorrectFileChannelTruncate() throws IOException {
436         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
437         FileChannel fc = input.getChannel();
438 
439         try {
440             fc.truncate(FILE_LENGTH + 1);
441             fail();
442         } catch (UnsupportedOperationException e) {
443             assertEquals(METHOD_NOT_SUPPORTED_MESSAGE, e.getMessage());
444         }
445     }
446 
447     @Test
testOffsetCorrectFileChannelForce()448     public void testOffsetCorrectFileChannelForce() throws IOException {
449         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
450         FileChannel fc = input.getChannel();
451 
452         try {
453             fc.force(true);
454             fail();
455         } catch (UnsupportedOperationException e) {
456             assertEquals(METHOD_NOT_SUPPORTED_MESSAGE, e.getMessage());
457         }
458     }
459 
460     @Test
testOffsetCorrectFileChannelLock()461     public void testOffsetCorrectFileChannelLock() throws IOException {
462         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
463         FileChannel fc = input.getChannel();
464 
465         try {
466             fc.lock(0, 4, true);
467             fail();
468         } catch (UnsupportedOperationException e) {
469             assertEquals(METHOD_NOT_SUPPORTED_MESSAGE, e.getMessage());
470         }
471     }
472 
473     @Test
testOffsetCorrectFileChannelTryLock()474     public void testOffsetCorrectFileChannelTryLock() throws IOException {
475         AssetFileDescriptor.AutoCloseInputStream input = getInputStream();
476         FileChannel fc = input.getChannel();
477 
478         try {
479             fc.tryLock(0, 4, true);
480             fail();
481         } catch (UnsupportedOperationException e) {
482             assertEquals(METHOD_NOT_SUPPORTED_MESSAGE, e.getMessage());
483         }
484     }
485 
getInputStream()486     private AssetFileDescriptor.AutoCloseInputStream getInputStream() throws IOException {
487         openInput(0, FILE_LENGTH);
488         AssetFileDescriptor fd = new AssetFileDescriptor(mFd.getParcelFileDescriptor(),
489                 0,
490                 FILE_LENGTH);
491         AssetFileDescriptor.AutoCloseInputStream input =
492                 new AssetFileDescriptor.AutoCloseInputStream(fd);
493         return input;
494     }
495 
496     @Test
testNonSeekableInputStream()497     public void testNonSeekableInputStream() throws Exception {
498         final FileDescriptor[] fds = Os.pipe();
499         AssetFileDescriptor readAFd = new AssetFileDescriptor(
500                 new ParcelFileDescriptor(fds[0]), 0, FILE_LENGTH);
501         FileDescriptor writeFd = fds[1];
502         FileOutputStream out = new FileOutputStream(writeFd);
503         out.write(FILE_DATA);
504         out.close();
505 
506         StructStat ss = Os.fstat(readAFd.getParcelFileDescriptor().getFileDescriptor());
507         assertTrue(S_ISFIFO(ss.st_mode));
508 
509         AssetFileDescriptor.AutoCloseInputStream in =
510                 new AssetFileDescriptor.AutoCloseInputStream(readAFd);
511         assertEquals(FILE_LENGTH, in.available());
512         assertEquals(FILE_DATA[0], in.read());
513         assertEquals(FILE_LENGTH - 1, in.available());
514 
515         byte[]buffer = new byte[2];
516         assertEquals(buffer.length, in.read(buffer));
517         assertEquals(FILE_DATA[3], in.read());
518         assertEquals(FILE_LENGTH - 4, in.available());
519 
520         assertEquals(1, in.skip(1));
521         assertEquals(FILE_DATA[5], in.read());
522         in.close();
523     }
524 }
525