1b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey/*
2b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * Copyright (C) 2013 The Android Open Source Project
3b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey *
4b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * you may not use this file except in compliance with the License.
6b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * You may obtain a copy of the License at
7b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey *
8b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey *
10b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * Unless required by applicable law or agreed to in writing, software
11b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * See the License for the specific language governing permissions and
14b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * limitations under the License.
15b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey */
16b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
17b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeypackage android.support.v4.content;
18b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
19b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport static android.provider.OpenableColumns.DISPLAY_NAME;
20b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport static android.provider.OpenableColumns.SIZE;
21b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
224c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikasimport static org.junit.Assert.assertEquals;
234c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikasimport static org.junit.Assert.fail;
244c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas
25b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.content.ContentResolver;
264c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikasimport android.content.Context;
27b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.database.Cursor;
28b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.net.Uri;
29b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.os.Environment;
304c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikasimport android.support.test.InstrumentationRegistry;
31754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.support.test.filters.SmallTest;
324c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikasimport android.support.test.runner.AndroidJUnit4;
33b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.support.v4.content.FileProvider.SimplePathStrategy;
34b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.test.MoreAsserts;
354c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas
364c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikasimport org.junit.Before;
374c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikasimport org.junit.Test;
384c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikasimport org.junit.runner.RunWith;
392cb3bffdfbe47f5657821e5a12da9aab16b85c52Jeff Sharkey
40120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viveretteimport java.io.ByteArrayOutputStream;
41b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport java.io.File;
42b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport java.io.FileNotFoundException;
43b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport java.io.FileOutputStream;
44120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viveretteimport java.io.IOException;
45b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport java.io.InputStream;
46b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport java.io.OutputStream;
47b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
48b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey/**
49b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * Tests for {@link FileProvider}
50b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey */
514c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas@SmallTest
524c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas@RunWith(AndroidJUnit4.class)
534c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikaspublic class FileProviderTest {
54b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private static final String TEST_AUTHORITY = "moocow";
55b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
56b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private static final String TEST_FILE = "file.test";
57b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private static final byte[] TEST_DATA = new byte[] { (byte) 0xf0, 0x00, 0x0d };
58b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private static final byte[] TEST_DATA_ALT = new byte[] { (byte) 0x33, 0x66 };
59b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
60b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private ContentResolver mResolver;
614c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    private Context mContext;
62b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
634c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Before
644c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    public void setup() throws Exception {
654c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas        mContext = InstrumentationRegistry.getTargetContext();
664c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas        mResolver = mContext.getContentResolver();
67b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
68b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
694c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
70b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyUriSimple() throws Exception {
71b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
72b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
73b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
74b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        File file = buildPath(mContext.getFilesDir(), "file.test");
75b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://authority/tag/file.test",
76b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(file).toString());
77b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
78b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        file = buildPath(mContext.getFilesDir(), "subdir", "file.test");
79b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://authority/tag/subdir/file.test",
80b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(file).toString());
81b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
82b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        file = buildPath(Environment.getExternalStorageDirectory(), "file.test");
83b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
84b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            strat.getUriForFile(file);
85b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            fail("somehow got uri for file outside roots?");
86b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } catch (IllegalArgumentException e) {
87b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
88b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
89b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
904c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
91b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyUriJumpOutside() throws Exception {
92b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
93b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
94b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
95b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        File file = buildPath(mContext.getFilesDir(), "..", "file.test");
96b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
97b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            strat.getUriForFile(file);
98b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            fail("file escaped!");
99b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } catch (IllegalArgumentException e) {
100b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
101b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
102b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1034c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
104b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyUriShortestRoot() throws Exception {
105b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        SimplePathStrategy strat = new SimplePathStrategy("authority");
106b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag1", mContext.getFilesDir());
107b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag2", new File("/"));
108b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
109b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        File file = buildPath(mContext.getFilesDir(), "file.test");
110b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://authority/tag1/file.test",
111b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(file).toString());
112b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
113b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat = new SimplePathStrategy("authority");
114b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag1", new File("/"));
115b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag2", mContext.getFilesDir());
116b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
117b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        file = buildPath(mContext.getFilesDir(), "file.test");
118b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://authority/tag2/file.test",
119b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(file).toString());
120b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
121b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1224c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
123b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyFileSimple() throws Exception {
124b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
125b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
126b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1272002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File expectedRoot = mContext.getFilesDir().getCanonicalFile();
1282002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File file = buildPath(expectedRoot, "file.test");
129b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(file.getPath(),
130b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getFileForUri(Uri.parse("content://authority/tag/file.test")).getPath());
131b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1322002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        file = buildPath(expectedRoot, "subdir", "file.test");
133b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(file.getPath(), strat.getFileForUri(
134b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                Uri.parse("content://authority/tag/subdir/file.test")).getPath());
135b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
136b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1374c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
138b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyFileJumpOutside() throws Exception {
139b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
140b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
141b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
142b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
143b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            strat.getFileForUri(Uri.parse("content://authority/tag/../file.test"));
144b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            fail("file escaped!");
145b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } catch (SecurityException e) {
146b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
147b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
148b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1494c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
150b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyEscaping() throws Exception {
151b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
152b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("t/g", mContext.getFilesDir());
153b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1542002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File expectedRoot = mContext.getFilesDir().getCanonicalFile();
1552002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File file = buildPath(expectedRoot, "lol\"wat?foo&bar", "wat.txt");
156b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final String expected = "content://authority/t%2Fg/lol%22wat%3Ffoo%26bar/wat.txt";
157b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
158b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(expected,
159b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(file).toString());
160b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(file.getPath(),
161b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getFileForUri(Uri.parse(expected)).getPath());
162b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
163b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1644c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
165b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyExtraParams() throws Exception {
166b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
167b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
168b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1692002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File expectedRoot = mContext.getFilesDir().getCanonicalFile();
1702002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File file = buildPath(expectedRoot, "file.txt");
171b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(file.getPath(), strat.getFileForUri(
172b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                Uri.parse("content://authority/tag/file.txt?extra=foo")).getPath());
173b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
174b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1754c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
176b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyExtraSeparators() throws Exception {
177b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
178b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
179b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
180b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        // When canonicalized, the path separators are trimmed
181b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        File inFile = new File(mContext.getFilesDir(), "//foo//bar//");
1822002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File expectedRoot = mContext.getFilesDir().getCanonicalFile();
1832002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File outFile = new File(expectedRoot, "/foo/bar");
184b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final String expected = "content://authority/tag/foo/bar";
185b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
186b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(expected,
187b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(inFile).toString());
188b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(outFile.getPath(),
189b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getFileForUri(Uri.parse(expected)).getPath());
190b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
191b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1924c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
193b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testQueryProjectionNull() throws Exception {
194b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
195b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
196b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
197b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        // Verify that null brings out default columns
198b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        Cursor cursor = mResolver.query(uri, null, null, null, null);
199b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
200b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(1, cursor.getCount());
201b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.moveToFirst();
202b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_FILE, cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)));
203b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_DATA.length, cursor.getLong(cursor.getColumnIndex(SIZE)));
204b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
205b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.close();
206b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
207b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
208b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
2094c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
210b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testQueryProjectionOrder() throws Exception {
211b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
212b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
213b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
214b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        // Verify that swapped order works
215b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        Cursor cursor = mResolver.query(uri, new String[] {
216b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                SIZE, DISPLAY_NAME }, null, null, null);
217b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
218b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(1, cursor.getCount());
219b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.moveToFirst();
220b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_DATA.length, cursor.getLong(0));
221b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_FILE, cursor.getString(1));
222b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
223b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.close();
224b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
225b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
226b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        cursor = mResolver.query(uri, new String[] {
227b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                DISPLAY_NAME, SIZE }, null, null, null);
228b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
229b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(1, cursor.getCount());
230b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.moveToFirst();
231b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_FILE, cursor.getString(0));
232b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_DATA.length, cursor.getLong(1));
233b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
234b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.close();
235b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
236b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
237b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
2384c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
239b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testQueryExtraColumn() throws Exception {
240b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
241b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
242b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
243b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        // Verify that extra column doesn't gook things up
244b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        Cursor cursor = mResolver.query(uri, new String[] {
245b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                SIZE, "foobar", DISPLAY_NAME }, null, null, null);
246b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
247b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(1, cursor.getCount());
248b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.moveToFirst();
249b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_DATA.length, cursor.getLong(0));
250b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_FILE, cursor.getString(1));
251b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
252b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.close();
253b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
254b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
255b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
2564c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
257b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testReadFile() throws Exception {
258b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
259b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
260b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
261b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertContentsEquals(TEST_DATA, uri);
262b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
263b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
2644c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
265b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testWriteFile() throws Exception {
266b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
267b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
268b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
269b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertContentsEquals(TEST_DATA, uri);
270b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
271b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final OutputStream out = mResolver.openOutputStream(uri);
272b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
273b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            out.write(TEST_DATA_ALT);
274b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
275120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            closeQuietly(out);
276b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
277b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
278b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertContentsEquals(TEST_DATA_ALT, uri);
279b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
280b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
2814c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
282b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testWriteMissingFile() throws Exception {
283b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
284b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, null);
285b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
286b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
287b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertContentsEquals(new byte[0], uri);
288b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            fail("Somehow read missing file?");
289b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } catch(FileNotFoundException e) {
290b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
291b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
292b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final OutputStream out = mResolver.openOutputStream(uri);
293b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
294b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            out.write(TEST_DATA_ALT);
295b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
296120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            closeQuietly(out);
297b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
298b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
299b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertContentsEquals(TEST_DATA_ALT, uri);
300b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
301b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
3024c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
303b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testDelete() throws Exception {
304b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
305b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
306b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
307b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertContentsEquals(TEST_DATA, uri);
308b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
309b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(1, mResolver.delete(uri, null, null));
310b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(0, mResolver.delete(uri, null, null));
311b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
312b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
313b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertContentsEquals(new byte[0], uri);
314b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            fail("Somehow read missing file?");
315b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } catch(FileNotFoundException e) {
316b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
317b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
318b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
3194c0cac8cbf22f47465eec7a75d723fa0e0ebf1dcAurimas Liutikas    @Test
320b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testMetaDataTargets() {
321b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        Uri actual;
322b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
323b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
324b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                new File("/proc/version"));
325b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_root/proc/version", actual.toString());
326b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
327b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
328b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                new File("/proc/1/mountinfo"));
329b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_init/mountinfo", actual.toString());
330b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
331b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
332b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                buildPath(mContext.getFilesDir(), "meow"));
333b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_files/meow", actual.toString());
334b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
335b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
336b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                buildPath(mContext.getFilesDir(), "thumbs", "rawr"));
337b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_thumbs/rawr", actual.toString());
338b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
339b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
340b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                buildPath(mContext.getCacheDir(), "up", "down"));
341b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_cache/up/down", actual.toString());
342b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
343b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
344b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                buildPath(Environment.getExternalStorageDirectory(), "Android", "obb", "foobar"));
345b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_external/Android/obb/foobar", actual.toString());
3462002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette
3472002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(mContext, null);
3482002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
3492002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette            buildPath(externalFilesDirs[0], "foo", "bar"));
3502002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        assertEquals("content://moocow/test_external_files/foo/bar", actual.toString());
3512002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette
3522002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File[] externalCacheDirs = ContextCompat.getExternalCacheDirs(mContext);
3532002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
3542002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette            buildPath(externalCacheDirs[0], "foo", "bar"));
3552002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        assertEquals("content://moocow/test_external_cache/foo/bar", actual.toString());
356b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
357b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
358b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private void assertContentsEquals(byte[] expected, Uri actual) throws Exception {
359b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final InputStream in = mResolver.openInputStream(actual);
360b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
361120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            MoreAsserts.assertEquals(expected, readFully(in));
362b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
363120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            closeQuietly(in);
364b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
365b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
366b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
367b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private Uri stageFileAndGetUri(File file, byte[] data) throws Exception {
368b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        if (data != null) {
369b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            final FileOutputStream out = new FileOutputStream(file);
370b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            try {
371b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                out.write(data);
372b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            } finally {
373b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                out.close();
374b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            }
375b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } else {
376b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            file.delete();
377b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
378b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        return FileProvider.getUriForFile(mContext, TEST_AUTHORITY, file);
379b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
380b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
381b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private static File buildPath(File base, String... segments) {
382b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        File cur = base;
383b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        for (String segment : segments) {
384b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            if (cur == null) {
385b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                cur = new File(segment);
386b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            } else {
387b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                cur = new File(cur, segment);
388b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            }
389b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
390b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        return cur;
391b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
392120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette
393120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    /**
394120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
395120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     */
396120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    private static void closeQuietly(AutoCloseable closeable) {
397120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        if (closeable != null) {
398120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            try {
399120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette                closeable.close();
400120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            } catch (RuntimeException rethrown) {
401120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette                throw rethrown;
402120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            } catch (Exception ignored) {
403120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            }
404120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        }
405120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    }
406120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette
407120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    /**
408120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     * Returns a byte[] containing the remainder of 'in', closing it when done.
409120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     */
410120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    private static byte[] readFully(InputStream in) throws IOException {
411120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        try {
412120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            return readFullyNoClose(in);
413120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        } finally {
414120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            in.close();
415120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        }
416120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    }
417120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette
418120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    /**
419120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     * Returns a byte[] containing the remainder of 'in'.
420120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     */
421120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    private static byte[] readFullyNoClose(InputStream in) throws IOException {
422120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
423120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        byte[] buffer = new byte[1024];
424120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        int count;
425120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        while ((count = in.read(buffer)) != -1) {
426120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            bytes.write(buffer, 0, count);
427120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        }
428120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        return bytes.toByteArray();
429120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    }
430b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey}
431