Home | History | Annotate | Download | only in cts
      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.os.cts;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertNotNull;
     21 import static org.junit.Assert.assertNull;
     22 import static org.junit.Assert.assertSame;
     23 import static org.junit.Assert.assertTrue;
     24 import static org.junit.Assert.fail;
     25 
     26 import android.content.Context;
     27 import android.os.Handler;
     28 import android.os.Looper;
     29 import android.os.Parcel;
     30 import android.os.ParcelFileDescriptor;
     31 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
     32 import android.os.Parcelable;
     33 import android.os.cts.ParcelFileDescriptorPeer.FutureCloseListener;
     34 import android.support.test.InstrumentationRegistry;
     35 import android.support.test.runner.AndroidJUnit4;
     36 import android.system.ErrnoException;
     37 import android.system.Os;
     38 import android.system.OsConstants;
     39 import android.test.MoreAsserts;
     40 
     41 import com.google.common.util.concurrent.AbstractFuture;
     42 
     43 import junit.framework.ComparisonFailure;
     44 
     45 import org.junit.Test;
     46 import org.junit.runner.RunWith;
     47 
     48 import java.io.File;
     49 import java.io.FileDescriptor;
     50 import java.io.FileInputStream;
     51 import java.io.FileOutputStream;
     52 import java.io.IOException;
     53 import java.io.InputStream;
     54 import java.io.OutputStream;
     55 import java.net.InetAddress;
     56 import java.net.ServerSocket;
     57 import java.net.Socket;
     58 import java.util.concurrent.TimeUnit;
     59 
     60 @RunWith(AndroidJUnit4.class)
     61 public class ParcelFileDescriptorTest {
     62     private static final long DURATION = 100l;
     63 
     64     private Context getContext() {
     65         return InstrumentationRegistry.getContext();
     66     }
     67 
     68     @Test
     69     public void testConstructorAndOpen() throws Exception {
     70         ParcelFileDescriptor tempFile = makeParcelFileDescriptor(getContext());
     71 
     72         ParcelFileDescriptor pfd = new ParcelFileDescriptor(tempFile);
     73         AutoCloseInputStream in = new AutoCloseInputStream(pfd);
     74         try {
     75             // read the data that was wrote previously
     76             assertEquals(0, in.read());
     77             assertEquals(1, in.read());
     78             assertEquals(2, in.read());
     79             assertEquals(3, in.read());
     80         } finally {
     81             in.close();
     82         }
     83     }
     84 
     85     private static class DoneSignal extends AbstractFuture<Void> {
     86         public boolean set() {
     87             return super.set(null);
     88         }
     89 
     90         @Override
     91         public boolean setException(Throwable t) {
     92             return super.setException(t);
     93         }
     94     }
     95 
     96     @Test
     97     public void testFromSocket() throws Throwable {
     98         final int PORT = 12222;
     99         final int DATA = 1;
    100 
    101         final DoneSignal done = new DoneSignal();
    102 
    103         final Thread t = new Thread(new Runnable() {
    104             @Override
    105             public void run() {
    106                 try {
    107                     ServerSocket ss;
    108                     ss = new ServerSocket(PORT);
    109                     Socket sSocket = ss.accept();
    110                     OutputStream out = sSocket.getOutputStream();
    111                     out.write(DATA);
    112                     Thread.sleep(DURATION);
    113                     out.close();
    114                     done.set();
    115                 } catch (Exception e) {
    116                     done.setException(e);
    117                 }
    118             }
    119         });
    120         t.start();
    121 
    122         Thread.sleep(DURATION);
    123         Socket socket;
    124         socket = new Socket(InetAddress.getLocalHost(), PORT);
    125         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
    126         AutoCloseInputStream in = new AutoCloseInputStream(pfd);
    127         assertEquals(DATA, in.read());
    128         in.close();
    129         socket.close();
    130         pfd.close();
    131 
    132         done.get(5, TimeUnit.SECONDS);
    133     }
    134 
    135     @Test
    136     public void testFromData() throws IOException {
    137         assertNull(ParcelFileDescriptor.fromData(null, null));
    138         byte[] data = new byte[] { 0 };
    139         assertFileDescriptorContent(data, ParcelFileDescriptor.fromData(data, null));
    140         data = new byte[] { 0, 1, 2, 3 };
    141         assertFileDescriptorContent(data, ParcelFileDescriptor.fromData(data, null));
    142 
    143         // Check that modifying the data does not modify the data in the FD
    144         data = new byte[] { 0, 1, 2, 3 };
    145         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromData(data, null);
    146         data[1] = 42;
    147         assertFileDescriptorContent(new byte[] { 0, 1, 2, 3 }, pfd);
    148     }
    149 
    150     private static void assertFileDescriptorContent(byte[] expected, ParcelFileDescriptor fd)
    151         throws IOException {
    152         assertInputStreamContent(expected, new ParcelFileDescriptor.AutoCloseInputStream(fd));
    153     }
    154 
    155     private static void assertInputStreamContent(byte[] expected, InputStream is)
    156             throws IOException {
    157         try {
    158             byte[] observed = new byte[expected.length];
    159             int count = is.read(observed);
    160             assertEquals(expected.length, count);
    161             assertEquals(-1, is.read());
    162             MoreAsserts.assertEquals(expected, observed);
    163         } finally {
    164             is.close();
    165         }
    166     }
    167 
    168     @Test
    169     public void testFromDataSkip() throws IOException {
    170         byte[] data = new byte[] { 40, 41, 42, 43, 44, 45, 46 };
    171         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromData(data, null);
    172         assertNotNull(pfd);
    173         FileDescriptor fd = pfd.getFileDescriptor();
    174         assertNotNull(fd);
    175         assertTrue(fd.valid());
    176         FileInputStream is = new FileInputStream(fd);
    177         try {
    178             assertEquals(1, is.skip(1));
    179             assertEquals(41, is.read());
    180             assertEquals(42, is.read());
    181             assertEquals(2, is.skip(2));
    182             assertEquals(45, is.read());
    183             assertEquals(46, is.read());
    184             assertEquals(-1, is.read());
    185         } finally {
    186             is.close();
    187         }
    188     }
    189 
    190     @Test
    191     public void testToString() {
    192         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
    193         assertNotNull(pfd.toString());
    194     }
    195 
    196     @Test
    197     public void testWriteToParcel() throws Exception {
    198         ParcelFileDescriptor pf = makeParcelFileDescriptor(getContext());
    199 
    200         Parcel pl = Parcel.obtain();
    201         pf.writeToParcel(pl, ParcelFileDescriptor.PARCELABLE_WRITE_RETURN_VALUE);
    202         pl.setDataPosition(0);
    203         ParcelFileDescriptor pfd = ParcelFileDescriptor.CREATOR.createFromParcel(pl);
    204         AutoCloseInputStream in = new AutoCloseInputStream(pfd);
    205         try {
    206             // read the data that was wrote previously
    207             assertEquals(0, in.read());
    208             assertEquals(1, in.read());
    209             assertEquals(2, in.read());
    210             assertEquals(3, in.read());
    211         } finally {
    212             in.close();
    213         }
    214     }
    215 
    216     @Test
    217     public void testClose() throws Exception {
    218         ParcelFileDescriptor pf = makeParcelFileDescriptor(getContext());
    219         AutoCloseInputStream in1 = new AutoCloseInputStream(pf);
    220         try {
    221             assertEquals(0, in1.read());
    222         } finally {
    223             in1.close();
    224         }
    225 
    226         pf.close();
    227 
    228         AutoCloseInputStream in2 = new AutoCloseInputStream(pf);
    229         try {
    230             assertEquals(0, in2.read());
    231             fail("Failed to throw exception.");
    232         } catch (Exception e) {
    233             // expected
    234         } finally {
    235             in2.close();
    236         }
    237     }
    238 
    239     @Test
    240     public void testGetStatSize() throws Exception {
    241         ParcelFileDescriptor pf = makeParcelFileDescriptor(getContext());
    242         assertTrue(pf.getStatSize() >= 0);
    243     }
    244 
    245     @Test
    246     public void testGetFileDescriptor() {
    247         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
    248         assertNotNull(pfd.getFileDescriptor());
    249 
    250         ParcelFileDescriptor p = new ParcelFileDescriptor(pfd);
    251         assertSame(pfd.getFileDescriptor(), p.getFileDescriptor());
    252     }
    253 
    254     @Test
    255     public void testDescribeContents() {
    256         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
    257         assertTrue((Parcelable.CONTENTS_FILE_DESCRIPTOR & pfd.describeContents()) != 0);
    258     }
    259 
    260     private static void assertContains(String expected, String actual) {
    261         if (actual.contains(expected)) return;
    262         throw new ComparisonFailure("", expected, actual);
    263     }
    264 
    265     private static void write(ParcelFileDescriptor pfd, int oneByte)  throws IOException{
    266         new FileOutputStream(pfd.getFileDescriptor()).write(oneByte);
    267     }
    268 
    269     // This method is unlikely to be used by clients, as clients use ContentResolver,
    270     // which builds AutoCloseInputStream under the hood rather than FileInputStream
    271     // built from a raw FD.
    272     //
    273     // Using new FileInputStream(PFD.getFileDescriptor()) is discouraged, as error
    274     // propagation is lost, and read() will never throw IOException in such case.
    275     private static int read(ParcelFileDescriptor pfd) throws IOException {
    276         return new FileInputStream(pfd.getFileDescriptor()).read();
    277     }
    278 
    279     @Test
    280     public void testPipeNormal() throws Exception {
    281         final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
    282         final ParcelFileDescriptor red = pipe[0];
    283         final ParcelFileDescriptor blue = pipe[1];
    284 
    285         write(blue, 1);
    286         assertEquals(1, read(red));
    287 
    288         blue.close();
    289         assertEquals(-1, read(red));
    290         red.checkError();
    291     }
    292 
    293     // Reading should be done via AutoCloseInputStream if possible, rather than
    294     // recreating a FileInputStream from a raw FD, what's done in read(PFD).
    295     @Test
    296     public void testPipeError_Discouraged() throws Exception {
    297         final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
    298         final ParcelFileDescriptor red = pipe[0];
    299         final ParcelFileDescriptor blue = pipe[1];
    300 
    301         write(blue, 2);
    302         blue.closeWithError("OMG MUFFINS");
    303 
    304         // Even though closed we should still drain pipe.
    305         assertEquals(2, read(red));
    306         assertEquals(-1, read(red));
    307         try {
    308             red.checkError();
    309             fail("expected throw!");
    310         } catch (IOException e) {
    311             assertContains("OMG MUFFINS", e.getMessage());
    312         }
    313     }
    314 
    315     @Test
    316     public void testPipeError() throws Exception {
    317         final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
    318         final ParcelFileDescriptor red = pipe[0];
    319         final ParcelFileDescriptor blue = pipe[1];
    320 
    321         write(blue, 2);
    322         blue.closeWithError("OMG MUFFINS");
    323 
    324         try (AutoCloseInputStream is = new AutoCloseInputStream(red)) {
    325             is.read();
    326             is.read();  // Checks errors on EOF.
    327             fail("expected throw!");
    328         } catch (IOException e) {
    329             assertContains("OMG MUFFINS", e.getMessage());
    330         }
    331     }
    332 
    333     @Test
    334     public void testFileNormal() throws Exception {
    335         final Handler handler = new Handler(Looper.getMainLooper());
    336         final FutureCloseListener listener = new FutureCloseListener();
    337         final ParcelFileDescriptor file = ParcelFileDescriptor.open(
    338                 File.createTempFile("pfd", "bbq"), ParcelFileDescriptor.MODE_READ_WRITE, handler,
    339                 listener);
    340 
    341         write(file, 7);
    342         file.close();
    343 
    344         // make sure we were notified
    345         assertEquals(null, listener.get());
    346     }
    347 
    348     @Test
    349     public void testFileError() throws Exception {
    350         final Handler handler = new Handler(Looper.getMainLooper());
    351         final FutureCloseListener listener = new FutureCloseListener();
    352         final ParcelFileDescriptor file = ParcelFileDescriptor.open(
    353                 File.createTempFile("pfd", "bbq"), ParcelFileDescriptor.MODE_READ_WRITE, handler,
    354                 listener);
    355 
    356         write(file, 8);
    357         file.closeWithError("OMG BANANAS");
    358 
    359         // make sure error came through
    360         assertContains("OMG BANANAS", listener.get().getMessage());
    361     }
    362 
    363     @Test
    364     public void testFileDetach() throws Exception {
    365         final Handler handler = new Handler(Looper.getMainLooper());
    366         final FutureCloseListener listener = new FutureCloseListener();
    367         final ParcelFileDescriptor file = ParcelFileDescriptor.open(
    368                 File.createTempFile("pfd", "bbq"), ParcelFileDescriptor.MODE_READ_WRITE, handler,
    369                 listener);
    370 
    371         file.detachFd();
    372 
    373         // make sure detach came through
    374         assertContains("DETACHED", listener.get().getMessage());
    375     }
    376 
    377     @Test
    378     public void testSocketErrorAfterClose() throws Exception {
    379         final ParcelFileDescriptor[] pair = ParcelFileDescriptor.createReliableSocketPair();
    380         final ParcelFileDescriptor red = pair[0];
    381         final ParcelFileDescriptor blue = pair[1];
    382 
    383         // both sides throw their hands in the air
    384         blue.closeWithError("BLUE RAWR");
    385         red.closeWithError("RED RAWR");
    386 
    387         // red noticed the blue error, but after that the comm pipe was dead so
    388         // blue had no way of seeing the red error.
    389         try {
    390             red.checkError();
    391             fail("expected throw!");
    392         } catch (IOException e) {
    393             assertContains("BLUE RAWR", e.getMessage());
    394         }
    395 
    396         // expected to not throw; no error
    397         blue.checkError();
    398     }
    399 
    400     @Test
    401     public void testSocketMultipleCheck() throws Exception {
    402         final ParcelFileDescriptor[] pair = ParcelFileDescriptor.createReliableSocketPair();
    403         final ParcelFileDescriptor red = pair[0];
    404         final ParcelFileDescriptor blue = pair[1];
    405 
    406         // allow checking before closed; they should all pass
    407         blue.checkError();
    408         blue.checkError();
    409         blue.checkError();
    410 
    411         // and verify we actually see it
    412         red.closeWithError("RAWR RED");
    413         try {
    414             blue.checkError();
    415             fail("expected throw!");
    416         } catch (IOException e) {
    417             assertContains("RAWR RED", e.getMessage());
    418         }
    419     }
    420 
    421     // http://b/21578056
    422     @Test
    423     public void testFileNamesWithNonBmpChars() throws Exception {
    424         final File file = File.createTempFile("treble_clef_\ud834\udd1e", ".tmp");
    425         final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
    426                 ParcelFileDescriptor.MODE_READ_ONLY);
    427         assertNotNull(pfd);
    428         pfd.close();
    429     }
    430 
    431     @Test
    432     public void testCheckFinalizerBehavior() throws Exception {
    433         final Runtime runtime = Runtime.getRuntime();
    434         ParcelFileDescriptor pfd = makeParcelFileDescriptor(getContext());
    435         assertTrue(checkIsValid(pfd.getFileDescriptor()));
    436 
    437         ParcelFileDescriptor wrappedPfd = new ParcelFileDescriptor(pfd);
    438         assertTrue(checkIsValid(wrappedPfd.getFileDescriptor()));
    439 
    440         FileDescriptor fd = pfd.getFileDescriptor();
    441         int rawFd = pfd.getFd();
    442         pfd = null;
    443         assertNull(pfd); // To keep tools happy - yes we are using the write to null
    444         runtime.gc(); runtime.runFinalization();
    445         assertTrue("Wrapped PFD failed to hold reference",
    446                 checkIsValid(wrappedPfd.getFileDescriptor()));
    447         assertTrue("FileDescriptor failed to hold reference", checkIsValid(fd));
    448 
    449         wrappedPfd = null;
    450         assertNull(wrappedPfd); // To keep tools happy - yes we are using the write to null
    451         runtime.gc(); runtime.runFinalization();
    452         // TODO: Enable this once b/65027998 is fixed
    453         //assertTrue("FileDescriptor failed to hold reference", checkIsValid(fd));
    454 
    455         fd = null;
    456         assertNull(fd); // To keep tools happy - yes we are using the write to null
    457         runtime.gc(); runtime.runFinalization();
    458 
    459         try {
    460             ParcelFileDescriptor.fromFd(rawFd);
    461             fail("FD leaked");
    462         } catch (IOException ex) {
    463             // Success
    464         }
    465     }
    466 
    467     boolean checkIsValid(FileDescriptor fd) {
    468         try {
    469             Os.fstat(fd);
    470             return true;
    471         } catch (ErrnoException e) {
    472             if (e.errno == OsConstants.EBADF) {
    473                 return false;
    474             } else {
    475                 fail(e.getMessage());
    476                 // not reached
    477                 return false;
    478             }
    479         }
    480     }
    481 
    482     static ParcelFileDescriptor makeParcelFileDescriptor(Context con) throws Exception {
    483         final String fileName = "testParcelFileDescriptor";
    484 
    485         FileOutputStream fout = null;
    486 
    487         fout = con.openFileOutput(fileName, Context.MODE_PRIVATE);
    488 
    489         try {
    490             fout.write(new byte[] { 0x0, 0x1, 0x2, 0x3 });
    491         } finally {
    492             fout.close();
    493         }
    494 
    495         File dir = con.getFilesDir();
    496         File file = new File(dir, fileName);
    497         ParcelFileDescriptor pf = null;
    498 
    499         pf = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
    500 
    501         return pf;
    502     }
    503 }
    504