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
22b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.content.ContentResolver;
23b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.database.Cursor;
24b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.net.Uri;
25b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.os.Environment;
26b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.support.v4.content.FileProvider.SimplePathStrategy;
27b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.test.AndroidTestCase;
28b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport android.test.MoreAsserts;
292cb3bffdfbe47f5657821e5a12da9aab16b85c52Jeff Sharkey
30120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viveretteimport java.io.ByteArrayOutputStream;
312002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viveretteimport java.io.Closeable;
32b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport java.io.File;
33b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport java.io.FileNotFoundException;
34b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport java.io.FileOutputStream;
35120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viveretteimport java.io.IOException;
36b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport java.io.InputStream;
37b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeyimport java.io.OutputStream;
38b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
39b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey/**
40b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey * Tests for {@link FileProvider}
41b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey */
42b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkeypublic class FileProviderTest extends AndroidTestCase {
43b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private static final String TEST_AUTHORITY = "moocow";
44b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
45b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private static final String TEST_FILE = "file.test";
46b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private static final byte[] TEST_DATA = new byte[] { (byte) 0xf0, 0x00, 0x0d };
47b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private static final byte[] TEST_DATA_ALT = new byte[] { (byte) 0x33, 0x66 };
48b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
49b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private ContentResolver mResolver;
50b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
51b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    @Override
52b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    protected void setUp() throws Exception {
53b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        super.setUp();
54b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
55b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        mResolver = getContext().getContentResolver();
56b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
57b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
58b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyUriSimple() throws Exception {
59b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
60b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
61b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
62b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        File file = buildPath(mContext.getFilesDir(), "file.test");
63b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://authority/tag/file.test",
64b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(file).toString());
65b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
66b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        file = buildPath(mContext.getFilesDir(), "subdir", "file.test");
67b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://authority/tag/subdir/file.test",
68b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(file).toString());
69b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
70b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        file = buildPath(Environment.getExternalStorageDirectory(), "file.test");
71b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
72b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            strat.getUriForFile(file);
73b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            fail("somehow got uri for file outside roots?");
74b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } catch (IllegalArgumentException e) {
75b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
76b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
77b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
78b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyUriJumpOutside() throws Exception {
79b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
80b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
81b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
82b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        File file = buildPath(mContext.getFilesDir(), "..", "file.test");
83b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
84b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            strat.getUriForFile(file);
85b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            fail("file escaped!");
86b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } catch (IllegalArgumentException e) {
87b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
88b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
89b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
90b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyUriShortestRoot() throws Exception {
91b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        SimplePathStrategy strat = new SimplePathStrategy("authority");
92b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag1", mContext.getFilesDir());
93b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag2", new File("/"));
94b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
95b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        File file = buildPath(mContext.getFilesDir(), "file.test");
96b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://authority/tag1/file.test",
97b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(file).toString());
98b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
99b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat = new SimplePathStrategy("authority");
100b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag1", new File("/"));
101b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag2", mContext.getFilesDir());
102b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
103b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        file = buildPath(mContext.getFilesDir(), "file.test");
104b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://authority/tag2/file.test",
105b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(file).toString());
106b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
107b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
108b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyFileSimple() throws Exception {
109b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
110b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
111b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1122002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File expectedRoot = mContext.getFilesDir().getCanonicalFile();
1132002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File file = buildPath(expectedRoot, "file.test");
114b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(file.getPath(),
115b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getFileForUri(Uri.parse("content://authority/tag/file.test")).getPath());
116b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1172002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        file = buildPath(expectedRoot, "subdir", "file.test");
118b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(file.getPath(), strat.getFileForUri(
119b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                Uri.parse("content://authority/tag/subdir/file.test")).getPath());
120b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
121b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
122b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyFileJumpOutside() throws Exception {
123b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
124b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
125b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
126b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
127b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            strat.getFileForUri(Uri.parse("content://authority/tag/../file.test"));
128b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            fail("file escaped!");
129b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } catch (SecurityException e) {
130b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
131b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
132b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
133b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyEscaping() throws Exception {
134b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
135b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("t/g", mContext.getFilesDir());
136b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1372002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File expectedRoot = mContext.getFilesDir().getCanonicalFile();
1382002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File file = buildPath(expectedRoot, "lol\"wat?foo&bar", "wat.txt");
139b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final String expected = "content://authority/t%2Fg/lol%22wat%3Ffoo%26bar/wat.txt";
140b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
141b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(expected,
142b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(file).toString());
143b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(file.getPath(),
144b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getFileForUri(Uri.parse(expected)).getPath());
145b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
146b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
147b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyExtraParams() throws Exception {
148b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
149b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
150b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
1512002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File expectedRoot = mContext.getFilesDir().getCanonicalFile();
1522002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File file = buildPath(expectedRoot, "file.txt");
153b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(file.getPath(), strat.getFileForUri(
154b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                Uri.parse("content://authority/tag/file.txt?extra=foo")).getPath());
155b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
156b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
157b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testStrategyExtraSeparators() throws Exception {
158b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final SimplePathStrategy strat = new SimplePathStrategy("authority");
159b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        strat.addRoot("tag", mContext.getFilesDir());
160b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
161b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        // When canonicalized, the path separators are trimmed
162b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        File inFile = new File(mContext.getFilesDir(), "//foo//bar//");
1632002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File expectedRoot = mContext.getFilesDir().getCanonicalFile();
1642002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File outFile = new File(expectedRoot, "/foo/bar");
165b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final String expected = "content://authority/tag/foo/bar";
166b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
167b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(expected,
168b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getUriForFile(inFile).toString());
169b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(outFile.getPath(),
170b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                strat.getFileForUri(Uri.parse(expected)).getPath());
171b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
172b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
173b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testQueryProjectionNull() throws Exception {
174b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
175b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
176b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
177b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        // Verify that null brings out default columns
178b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        Cursor cursor = mResolver.query(uri, null, null, null, null);
179b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
180b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(1, cursor.getCount());
181b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.moveToFirst();
182b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_FILE, cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)));
183b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_DATA.length, cursor.getLong(cursor.getColumnIndex(SIZE)));
184b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
185b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.close();
186b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
187b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
188b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
189b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testQueryProjectionOrder() throws Exception {
190b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
191b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
192b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
193b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        // Verify that swapped order works
194b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        Cursor cursor = mResolver.query(uri, new String[] {
195b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                SIZE, DISPLAY_NAME }, null, null, null);
196b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
197b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(1, cursor.getCount());
198b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.moveToFirst();
199b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_DATA.length, cursor.getLong(0));
200b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_FILE, cursor.getString(1));
201b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
202b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.close();
203b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
204b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
205b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        cursor = mResolver.query(uri, new String[] {
206b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                DISPLAY_NAME, SIZE }, null, null, null);
207b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
208b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(1, cursor.getCount());
209b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.moveToFirst();
210b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_FILE, cursor.getString(0));
211b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_DATA.length, cursor.getLong(1));
212b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
213b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.close();
214b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
215b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
216b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
217b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testQueryExtraColumn() throws Exception {
218b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
219b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
220b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
221b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        // Verify that extra column doesn't gook things up
222b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        Cursor cursor = mResolver.query(uri, new String[] {
223b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                SIZE, "foobar", DISPLAY_NAME }, null, null, null);
224b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
225b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(1, cursor.getCount());
226b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.moveToFirst();
227b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_DATA.length, cursor.getLong(0));
228b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertEquals(TEST_FILE, cursor.getString(1));
229b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
230b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            cursor.close();
231b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
232b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
233b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
234b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testReadFile() throws Exception {
235b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
236b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
237b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
238b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertContentsEquals(TEST_DATA, uri);
239b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
240b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
241b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testWriteFile() throws Exception {
242b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
243b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
244b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
245b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertContentsEquals(TEST_DATA, uri);
246b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
247b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final OutputStream out = mResolver.openOutputStream(uri);
248b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
249b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            out.write(TEST_DATA_ALT);
250b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
251120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            closeQuietly(out);
252b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
253b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
254b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertContentsEquals(TEST_DATA_ALT, uri);
255b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
256b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
257b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testWriteMissingFile() throws Exception {
258b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
259b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, null);
260b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
261b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
262b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            assertContentsEquals(new byte[0], uri);
263b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            fail("Somehow read missing file?");
264b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } catch(FileNotFoundException e) {
265b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
266b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
267b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final OutputStream out = mResolver.openOutputStream(uri);
268b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
269b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            out.write(TEST_DATA_ALT);
270b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
271120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            closeQuietly(out);
272b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
273b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
274b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertContentsEquals(TEST_DATA_ALT, uri);
275b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
276b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
277b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testDelete() throws Exception {
278b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final File file = new File(mContext.getFilesDir(), TEST_FILE);
279b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final Uri uri = stageFileAndGetUri(file, TEST_DATA);
280b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
281b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertContentsEquals(TEST_DATA, uri);
282b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
283b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(1, mResolver.delete(uri, null, null));
284b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals(0, mResolver.delete(uri, null, 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
293b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    public void testMetaDataTargets() {
294b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        Uri actual;
295b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
296b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
297b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                new File("/proc/version"));
298b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_root/proc/version", actual.toString());
299b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
300b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
301b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                new File("/proc/1/mountinfo"));
302b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_init/mountinfo", actual.toString());
303b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
304b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
305b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                buildPath(mContext.getFilesDir(), "meow"));
306b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_files/meow", actual.toString());
307b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
308b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
309b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                buildPath(mContext.getFilesDir(), "thumbs", "rawr"));
310b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_thumbs/rawr", actual.toString());
311b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
312b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
313b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                buildPath(mContext.getCacheDir(), "up", "down"));
314b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_cache/up/down", actual.toString());
315b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
316b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
317b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                buildPath(Environment.getExternalStorageDirectory(), "Android", "obb", "foobar"));
318b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        assertEquals("content://moocow/test_external/Android/obb/foobar", actual.toString());
3192002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette
3202002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(mContext, null);
3212002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
3222002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette            buildPath(externalFilesDirs[0], "foo", "bar"));
3232002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        assertEquals("content://moocow/test_external_files/foo/bar", actual.toString());
3242002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette
3252002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        File[] externalCacheDirs = ContextCompat.getExternalCacheDirs(mContext);
3262002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        actual = FileProvider.getUriForFile(mContext, TEST_AUTHORITY,
3272002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette            buildPath(externalCacheDirs[0], "foo", "bar"));
3282002bed7d4769fc985b7a8e8b5ba875ffc7e829aAlan Viverette        assertEquals("content://moocow/test_external_cache/foo/bar", actual.toString());
329b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
330b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
331b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private void assertContentsEquals(byte[] expected, Uri actual) throws Exception {
332b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        final InputStream in = mResolver.openInputStream(actual);
333b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        try {
334120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            MoreAsserts.assertEquals(expected, readFully(in));
335b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } finally {
336120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            closeQuietly(in);
337b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
338b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
339b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
340b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private Uri stageFileAndGetUri(File file, byte[] data) throws Exception {
341b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        if (data != null) {
342b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            final FileOutputStream out = new FileOutputStream(file);
343b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            try {
344b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                out.write(data);
345b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            } finally {
346b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                out.close();
347b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            }
348b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        } else {
349b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            file.delete();
350b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
351b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        return FileProvider.getUriForFile(mContext, TEST_AUTHORITY, file);
352b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
353b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey
354b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    private static File buildPath(File base, String... segments) {
355b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        File cur = base;
356b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        for (String segment : segments) {
357b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            if (cur == null) {
358b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                cur = new File(segment);
359b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            } else {
360b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey                cur = new File(cur, segment);
361b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey            }
362b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        }
363b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey        return cur;
364b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey    }
365120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette
366120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    /**
367120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
368120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     */
369120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    private static void closeQuietly(AutoCloseable closeable) {
370120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        if (closeable != null) {
371120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            try {
372120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette                closeable.close();
373120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            } catch (RuntimeException rethrown) {
374120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette                throw rethrown;
375120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            } catch (Exception ignored) {
376120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            }
377120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        }
378120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    }
379120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette
380120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    /**
381120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     * Returns a byte[] containing the remainder of 'in', closing it when done.
382120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     */
383120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    private static byte[] readFully(InputStream in) throws IOException {
384120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        try {
385120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            return readFullyNoClose(in);
386120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        } finally {
387120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            in.close();
388120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        }
389120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    }
390120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette
391120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    /**
392120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     * Returns a byte[] containing the remainder of 'in'.
393120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette     */
394120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    private static byte[] readFullyNoClose(InputStream in) throws IOException {
395120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
396120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        byte[] buffer = new byte[1024];
397120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        int count;
398120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        while ((count = in.read(buffer)) != -1) {
399120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette            bytes.write(buffer, 0, count);
400120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        }
401120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette        return bytes.toByteArray();
402120325c4bd854c49fa50b872bdd84a7fac58210cAlan Viverette    }
403b49cb25e1450ae36d2559e9753c6d5064e530fccJeff Sharkey}
404