• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 com.android.cts.documentclient;
18 
19 import android.content.ContentResolver;
20 import android.content.Intent;
21 import android.content.pm.PackageManager;
22 import android.database.Cursor;
23 import android.net.Uri;
24 import android.os.SystemClock;
25 import android.provider.DocumentsContract;
26 import android.provider.DocumentsContract.Document;
27 import android.provider.DocumentsProvider;
28 import android.support.test.uiautomator.UiDevice;
29 import android.support.test.uiautomator.UiObject;
30 import android.support.test.uiautomator.UiSelector;
31 import android.test.InstrumentationTestCase;
32 import android.test.MoreAsserts;
33 import android.text.format.DateUtils;
34 
35 import com.android.cts.documentclient.MyActivity.Result;
36 
37 import java.io.ByteArrayOutputStream;
38 import java.io.FileNotFoundException;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.OutputStream;
42 
43 /**
44  * Tests for {@link DocumentsProvider} and interaction with platform intents
45  * like {@link Intent#ACTION_OPEN_DOCUMENT}.
46  */
47 public class DocumentsClientTest extends InstrumentationTestCase {
48     private UiDevice mDevice;
49     private MyActivity mActivity;
50 
51     private static final long TIMEOUT = 10 * DateUtils.SECOND_IN_MILLIS;
52 
53     @Override
setUp()54     public void setUp() throws Exception {
55         super.setUp();
56 
57         mDevice = UiDevice.getInstance(getInstrumentation());
58         mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
59                 MyActivity.class, null);
60         mDevice.waitForIdle();
61     }
62 
63     @Override
tearDown()64     public void tearDown() throws Exception {
65         super.tearDown();
66         mActivity.finish();
67     }
68 
testOpenSimple()69     public void testOpenSimple() throws Exception {
70         if (!supportedHardware()) return;
71 
72         try {
73             // Opening without permission should fail
74             readFully(Uri.parse("content://com.android.cts.documentprovider/document/doc:file1"));
75             fail("Able to read data before opened!");
76         } catch (SecurityException expected) {
77         }
78 
79         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
80         intent.addCategory(Intent.CATEGORY_OPENABLE);
81         intent.setType("*/*");
82         mActivity.startActivityForResult(intent, 42);
83 
84         // Ensure that we see both of our roots
85         mDevice.waitForIdle();
86         assertTrue("CtsLocal root", new UiObject(new UiSelector().text("CtsLocal")).waitForExists(TIMEOUT));
87         assertTrue("CtsCreate root", new UiObject(new UiSelector().text("CtsCreate")).exists());
88         assertFalse("CtsGetContent", new UiObject(new UiSelector().text("CtsGetContent")).exists());
89 
90         // Pick a specific file from our test provider
91         mDevice.waitForIdle();
92         new UiObject(new UiSelector().text("CtsLocal")).click();
93 
94         mDevice.waitForIdle();
95         new UiObject(new UiSelector().text("FILE1")).click();
96 
97         final Result result = mActivity.getResult();
98         final Uri uri = result.data.getData();
99 
100         // We should now have permission to read/write
101         MoreAsserts.assertEquals("fileone".getBytes(), readFully(uri));
102 
103         writeFully(uri, "replaced!".getBytes());
104         SystemClock.sleep(500);
105         MoreAsserts.assertEquals("replaced!".getBytes(), readFully(uri));
106     }
107 
testCreateNew()108     public void testCreateNew() throws Exception {
109         if (!supportedHardware()) return;
110 
111         final String DISPLAY_NAME = "My New Awesome Document Title";
112         final String MIME_TYPE = "image/png";
113 
114         final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
115         intent.addCategory(Intent.CATEGORY_OPENABLE);
116         intent.putExtra(Intent.EXTRA_TITLE, DISPLAY_NAME);
117         intent.setType(MIME_TYPE);
118         mActivity.startActivityForResult(intent, 42);
119 
120         mDevice.waitForIdle();
121         new UiObject(new UiSelector().text("CtsCreate")).click();
122         mDevice.waitForIdle();
123         new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
124                 .childSelector(new UiSelector().resourceId("android:id/button1"))).click();
125 
126         final Result result = mActivity.getResult();
127         final Uri uri = result.data.getData();
128 
129         writeFully(uri, "meow!".getBytes());
130 
131         assertEquals(DISPLAY_NAME, getColumn(uri, Document.COLUMN_DISPLAY_NAME));
132         assertEquals(MIME_TYPE, getColumn(uri, Document.COLUMN_MIME_TYPE));
133     }
134 
testCreateExisting()135     public void testCreateExisting() throws Exception {
136         if (!supportedHardware()) return;
137 
138         final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
139         intent.addCategory(Intent.CATEGORY_OPENABLE);
140         intent.putExtra(Intent.EXTRA_TITLE, "NEVERUSED");
141         intent.setType("mime2/file2");
142         mActivity.startActivityForResult(intent, 42);
143 
144         mDevice.waitForIdle();
145         new UiObject(new UiSelector().text("CtsCreate")).click();
146 
147         // Pick file2, which should be selected since MIME matches, then try
148         // picking a non-matching MIME, which should leave file2 selected.
149         mDevice.waitForIdle();
150         new UiObject(new UiSelector().text("FILE2")).click();
151         mDevice.waitForIdle();
152         new UiObject(new UiSelector().text("FILE1")).click();
153 
154         mDevice.waitForIdle();
155         new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
156                 .childSelector(new UiSelector().resourceId("android:id/button1"))).click();
157 
158         final Result result = mActivity.getResult();
159         final Uri uri = result.data.getData();
160 
161         MoreAsserts.assertEquals("filetwo".getBytes(), readFully(uri));
162     }
163 
testTree()164     public void testTree() throws Exception {
165         if (!supportedHardware()) return;
166 
167         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
168         mActivity.startActivityForResult(intent, 42);
169 
170         mDevice.waitForIdle();
171         new UiObject(new UiSelector().text("CtsCreate")).click();
172         mDevice.waitForIdle();
173         new UiObject(new UiSelector().text("DIR2")).click();
174         mDevice.waitForIdle();
175         new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
176                 .childSelector(new UiSelector().resourceId("android:id/button1"))).click();
177 
178         final Result result = mActivity.getResult();
179         final Uri uri = result.data.getData();
180 
181         // We should have selected DIR2
182         Uri doc = DocumentsContract.buildDocumentUriUsingTree(uri,
183                 DocumentsContract.getTreeDocumentId(uri));
184         Uri children = DocumentsContract.buildChildDocumentsUriUsingTree(uri,
185                 DocumentsContract.getTreeDocumentId(uri));
186 
187         assertEquals("DIR2", getColumn(doc, Document.COLUMN_DISPLAY_NAME));
188 
189         // Look around and make sure we can see children
190         final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
191         Cursor cursor = resolver.query(children, new String[] {
192                 Document.COLUMN_DISPLAY_NAME }, null, null, null);
193         try {
194             assertEquals(1, cursor.getCount());
195             assertTrue(cursor.moveToFirst());
196             assertEquals("FILE4", cursor.getString(0));
197         } finally {
198             cursor.close();
199         }
200 
201         // Create some documents
202         Uri pic = DocumentsContract.createDocument(resolver, doc, "image/png", "pic.png");
203         Uri dir = DocumentsContract.createDocument(resolver, doc, Document.MIME_TYPE_DIR, "my dir");
204         Uri dirPic = DocumentsContract.createDocument(resolver, dir, "image/png", "pic2.png");
205 
206         writeFully(pic, "pic".getBytes());
207         writeFully(dirPic, "dirPic".getBytes());
208 
209         // Read then delete existing doc
210         final Uri file4 = DocumentsContract.buildDocumentUriUsingTree(uri, "doc:file4");
211         MoreAsserts.assertEquals("filefour".getBytes(), readFully(file4));
212         assertTrue("delete", DocumentsContract.deleteDocument(resolver, file4));
213         try {
214             MoreAsserts.assertEquals("filefour".getBytes(), readFully(file4));
215             fail("Expected file to be gone");
216         } catch (FileNotFoundException expected) {
217         }
218 
219         // And rename something
220         dirPic = DocumentsContract.renameDocument(resolver, dirPic, "wow");
221         assertNotNull("rename", dirPic);
222 
223         // We should only see single child
224         assertEquals("wow", getColumn(dirPic, Document.COLUMN_DISPLAY_NAME));
225         MoreAsserts.assertEquals("dirPic".getBytes(), readFully(dirPic));
226 
227         try {
228             // Make sure we can't see files outside selected dir
229             getColumn(DocumentsContract.buildDocumentUriUsingTree(uri, "doc:file1"),
230                     Document.COLUMN_DISPLAY_NAME);
231             fail("Somehow read document outside tree!");
232         } catch (SecurityException expected) {
233         }
234     }
235 
testGetContent()236     public void testGetContent() throws Exception {
237         if (!supportedHardware()) return;
238 
239         final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
240         intent.addCategory(Intent.CATEGORY_OPENABLE);
241         intent.setType("*/*");
242         mActivity.startActivityForResult(intent, 42);
243 
244         // Look around, we should be able to see both DocumentsProviders and
245         // other GET_CONTENT sources.
246         mDevice.waitForIdle();
247         assertTrue("CtsLocal root", new UiObject(new UiSelector().text("CtsLocal")).waitForExists(TIMEOUT));
248         assertTrue("CtsCreate root", new UiObject(new UiSelector().text("CtsCreate")).exists());
249         assertTrue("CtsGetContent", new UiObject(new UiSelector().text("CtsGetContent")).exists());
250 
251         mDevice.waitForIdle();
252         new UiObject(new UiSelector().text("CtsGetContent")).click();
253 
254         final Result result = mActivity.getResult();
255         assertEquals("ReSuLt", result.data.getAction());
256     }
257 
getColumn(Uri uri, String column)258     private String getColumn(Uri uri, String column) {
259         final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
260         final Cursor cursor = resolver.query(uri, new String[] { column }, null, null, null);
261         try {
262             assertTrue(cursor.moveToFirst());
263             return cursor.getString(0);
264         } finally {
265             cursor.close();
266         }
267     }
268 
readFully(Uri uri)269     private byte[] readFully(Uri uri) throws IOException {
270         InputStream in = getInstrumentation().getContext().getContentResolver()
271                 .openInputStream(uri);
272         try {
273             ByteArrayOutputStream bytes = new ByteArrayOutputStream();
274             byte[] buffer = new byte[1024];
275             int count;
276             while ((count = in.read(buffer)) != -1) {
277                 bytes.write(buffer, 0, count);
278             }
279             return bytes.toByteArray();
280         } finally {
281             in.close();
282         }
283     }
284 
writeFully(Uri uri, byte[] data)285     private void writeFully(Uri uri, byte[] data) throws IOException {
286         OutputStream out = getInstrumentation().getContext().getContentResolver()
287                 .openOutputStream(uri);
288         try {
289             out.write(data);
290         } finally {
291             out.close();
292         }
293     }
294 
supportedHardware()295     private boolean supportedHardware() {
296         final PackageManager pm = getInstrumentation().getContext().getPackageManager();
297         if (pm.hasSystemFeature("android.hardware.type.television")
298                 || pm.hasSystemFeature("android.hardware.type.watch")) {
299             return false;
300         }
301         return true;
302     }
303 }
304