1 /* 2 * Copyright (C) 2016 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.documentsui.archives; 18 19 import com.android.documentsui.archives.ArchivesProvider; 20 import com.android.documentsui.archives.Archive; 21 import com.android.documentsui.tests.R; 22 23 import android.content.ContentProviderClient; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.database.ContentObserver; 27 import android.database.Cursor; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.os.ParcelFileDescriptor; 31 import android.provider.DocumentsContract.Document; 32 import android.provider.DocumentsContract; 33 import android.support.test.InstrumentationRegistry; 34 import android.test.AndroidTestCase; 35 import android.test.suitebuilder.annotation.MediumTest; 36 import android.text.TextUtils; 37 38 import java.io.File; 39 import java.io.FileOutputStream; 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.util.Scanner; 43 import java.util.concurrent.ExecutorService; 44 import java.util.concurrent.Executors; 45 import java.util.concurrent.TimeUnit; 46 import java.util.concurrent.CountDownLatch; 47 48 @MediumTest 49 public class ArchivesProviderTest extends AndroidTestCase { 50 private static final Uri ARCHIVE_URI = Uri.parse("content://i/love/strawberries"); 51 private static final String NOTIFICATION_URI = 52 "content://com.android.documentsui.archives/notification-uri"; 53 private ExecutorService mExecutor = null; 54 private Archive mArchive = null; 55 private TestUtils mTestUtils = null; 56 57 @Override setUp()58 public void setUp() throws Exception { 59 super.setUp(); 60 mExecutor = Executors.newSingleThreadExecutor(); 61 mTestUtils = new TestUtils(InstrumentationRegistry.getTargetContext(), 62 InstrumentationRegistry.getContext(), mExecutor); 63 } 64 65 @Override tearDown()66 public void tearDown() throws Exception { 67 mExecutor.shutdown(); 68 assertTrue(mExecutor.awaitTermination(3 /* timeout */, TimeUnit.SECONDS)); 69 super.tearDown(); 70 } 71 testQueryRoots()72 public void testQueryRoots() throws InterruptedException { 73 final ContentResolver resolver = getContext().getContentResolver(); 74 final Uri rootsUri = DocumentsContract.buildRootsUri(ArchivesProvider.AUTHORITY); 75 try (final ContentProviderClient client = resolver.acquireUnstableContentProviderClient( 76 rootsUri)) { 77 final Cursor cursor = resolver.query(rootsUri, null, null, null, null, null); 78 assertNotNull("Cursor must not be null.", cursor); 79 assertEquals(0, cursor.getCount()); 80 } 81 } 82 testOpen_Success()83 public void testOpen_Success() throws InterruptedException { 84 final Uri sourceUri = DocumentsContract.buildDocumentUri( 85 ResourcesProvider.AUTHORITY, "archive.zip"); 86 final Uri archiveUri = ArchivesProvider.buildUriForArchive(sourceUri, 87 ParcelFileDescriptor.MODE_READ_ONLY); 88 89 final Uri childrenUri = DocumentsContract.buildChildDocumentsUri( 90 ArchivesProvider.AUTHORITY, DocumentsContract.getDocumentId(archiveUri)); 91 92 final ContentResolver resolver = getContext().getContentResolver(); 93 final CountDownLatch latch = new CountDownLatch(1); 94 95 final ContentProviderClient client = resolver.acquireUnstableContentProviderClient( 96 archiveUri); 97 ArchivesProvider.acquireArchive(client, archiveUri); 98 99 { 100 final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null); 101 assertNotNull("Cursor must not be null. File not found?", cursor); 102 103 assertEquals(0, cursor.getCount()); 104 final Bundle extras = cursor.getExtras(); 105 assertEquals(true, extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)); 106 assertNull(extras.getString(DocumentsContract.EXTRA_ERROR)); 107 108 final Uri notificationUri = cursor.getNotificationUri(); 109 assertNotNull(notificationUri); 110 111 resolver.registerContentObserver(notificationUri, false, new ContentObserver(null) { 112 @Override 113 public void onChange(boolean selfChange, Uri uri) { 114 latch.countDown(); 115 } 116 }); 117 } 118 119 latch.await(30, TimeUnit.SECONDS); 120 { 121 final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null); 122 assertNotNull("Cursor must not be null. File not found?", cursor); 123 124 assertEquals(3, cursor.getCount()); 125 final Bundle extras = cursor.getExtras(); 126 assertEquals(false, extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)); 127 assertNull(extras.getString(DocumentsContract.EXTRA_ERROR)); 128 } 129 130 ArchivesProvider.releaseArchive(client, archiveUri); 131 client.release(); 132 } 133 testOpen_Failure()134 public void testOpen_Failure() throws InterruptedException { 135 final Uri sourceUri = DocumentsContract.buildDocumentUri( 136 ResourcesProvider.AUTHORITY, "broken.zip"); 137 final Uri archiveUri = ArchivesProvider.buildUriForArchive(sourceUri, 138 ParcelFileDescriptor.MODE_READ_ONLY); 139 140 final Uri childrenUri = DocumentsContract.buildChildDocumentsUri( 141 ArchivesProvider.AUTHORITY, DocumentsContract.getDocumentId(archiveUri)); 142 143 final ContentResolver resolver = getContext().getContentResolver(); 144 final CountDownLatch latch = new CountDownLatch(1); 145 146 final ContentProviderClient client = resolver.acquireUnstableContentProviderClient( 147 archiveUri); 148 ArchivesProvider.acquireArchive(client, archiveUri); 149 150 { 151 // TODO: Close this and any other cursor in this file. 152 final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null); 153 assertNotNull("Cursor must not be null. File not found?", cursor); 154 155 assertEquals(0, cursor.getCount()); 156 final Bundle extras = cursor.getExtras(); 157 assertEquals(true, extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)); 158 assertNull(extras.getString(DocumentsContract.EXTRA_ERROR)); 159 160 final Uri notificationUri = cursor.getNotificationUri(); 161 assertNotNull(notificationUri); 162 163 resolver.registerContentObserver(notificationUri, false, new ContentObserver(null) { 164 @Override 165 public void onChange(boolean selfChange, Uri uri) { 166 latch.countDown(); 167 } 168 }); 169 } 170 171 latch.await(30, TimeUnit.SECONDS); 172 { 173 final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null); 174 assertNotNull("Cursor must not be null. File not found?", cursor); 175 176 assertEquals(0, cursor.getCount()); 177 final Bundle extras = cursor.getExtras(); 178 assertEquals(false, extras.getBoolean(DocumentsContract.EXTRA_LOADING, false)); 179 assertFalse(TextUtils.isEmpty(extras.getString(DocumentsContract.EXTRA_ERROR))); 180 } 181 182 ArchivesProvider.releaseArchive(client, archiveUri); 183 client.release(); 184 } 185 testOpen_ClosesOnRelease()186 public void testOpen_ClosesOnRelease() throws InterruptedException { 187 final Uri sourceUri = DocumentsContract.buildDocumentUri( 188 ResourcesProvider.AUTHORITY, "broken.zip"); 189 final Uri archiveUri = ArchivesProvider.buildUriForArchive(sourceUri, 190 ParcelFileDescriptor.MODE_READ_ONLY); 191 192 final Uri childrenUri = DocumentsContract.buildChildDocumentsUri( 193 ArchivesProvider.AUTHORITY, DocumentsContract.getDocumentId(archiveUri)); 194 195 final ContentResolver resolver = getContext().getContentResolver(); 196 final CountDownLatch latch = new CountDownLatch(1); 197 198 final ContentProviderClient client = resolver.acquireUnstableContentProviderClient( 199 archiveUri); 200 201 // Acquire twice to ensure that the refcount works correctly. 202 ArchivesProvider.acquireArchive(client, archiveUri); 203 ArchivesProvider.acquireArchive(client, archiveUri); 204 205 { 206 final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null); 207 assertNotNull("Cursor must not be null. File not found?", cursor); 208 } 209 210 ArchivesProvider.releaseArchive(client, archiveUri); 211 212 { 213 final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null); 214 assertNotNull("Cursor must not be null. File not found?", cursor); 215 } 216 217 ArchivesProvider.releaseArchive(client, archiveUri); 218 219 try { 220 resolver.query(childrenUri, null, null, null, null, null); 221 fail("The archive was expected to be invalited on the last release call."); 222 } catch (IllegalStateException e) { 223 // Expected. 224 } 225 226 client.release(); 227 } 228 } 229