1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.provider.cts; 18 19 import static android.provider.DocumentsContract.buildChildDocumentsUri; 20 import static android.provider.DocumentsContract.buildDocumentUri; 21 import static android.provider.DocumentsContract.buildDocumentUriUsingTree; 22 import static android.provider.DocumentsContract.buildRecentDocumentsUri; 23 import static android.provider.DocumentsContract.buildRootUri; 24 import static android.provider.DocumentsContract.buildRootsUri; 25 import static android.provider.DocumentsContract.buildSearchDocumentsUri; 26 import static android.provider.DocumentsContract.buildTreeDocumentUri; 27 import static android.provider.DocumentsContract.copyDocument; 28 import static android.provider.DocumentsContract.createDocument; 29 import static android.provider.DocumentsContract.createWebLinkIntent; 30 import static android.provider.DocumentsContract.deleteDocument; 31 import static android.provider.DocumentsContract.ejectRoot; 32 import static android.provider.DocumentsContract.findDocumentPath; 33 import static android.provider.DocumentsContract.getDocumentMetadata; 34 import static android.provider.DocumentsContract.isChildDocument; 35 import static android.provider.DocumentsContract.moveDocument; 36 import static android.provider.DocumentsContract.removeDocument; 37 import static android.provider.DocumentsContract.renameDocument; 38 39 import static org.junit.Assert.assertArrayEquals; 40 import static org.junit.Assert.assertEquals; 41 import static org.junit.Assert.assertFalse; 42 import static org.junit.Assert.assertTrue; 43 import static org.junit.Assert.fail; 44 import static org.mockito.ArgumentMatchers.any; 45 import static org.mockito.ArgumentMatchers.anyInt; 46 import static org.mockito.Mockito.CALLS_REAL_METHODS; 47 import static org.mockito.Mockito.RETURNS_DEFAULTS; 48 import static org.mockito.Mockito.doNothing; 49 import static org.mockito.Mockito.doReturn; 50 import static org.mockito.Mockito.doThrow; 51 import static org.mockito.Mockito.mock; 52 53 import android.app.PendingIntent; 54 import android.content.ContentResolver; 55 import android.content.Context; 56 import android.content.Intent; 57 import android.content.IntentSender; 58 import android.content.pm.PackageManager; 59 import android.content.pm.ProviderInfo; 60 import android.content.pm.ResolveInfo; 61 import android.content.res.AssetFileDescriptor; 62 import android.database.Cursor; 63 import android.database.MatrixCursor; 64 import android.graphics.Bitmap; 65 import android.graphics.Canvas; 66 import android.graphics.Color; 67 import android.graphics.Point; 68 import android.net.Uri; 69 import android.os.Bundle; 70 import android.os.ParcelFileDescriptor; 71 import android.provider.DocumentsContract; 72 import android.provider.DocumentsContract.Path; 73 import android.provider.DocumentsProvider; 74 75 import androidx.test.InstrumentationRegistry; 76 import androidx.test.runner.AndroidJUnit4; 77 78 import org.junit.Before; 79 import org.junit.Test; 80 import org.junit.runner.RunWith; 81 82 import java.io.File; 83 import java.io.FileOutputStream; 84 import java.io.OutputStream; 85 import java.util.ArrayList; 86 import java.util.Arrays; 87 import java.util.List; 88 89 @RunWith(AndroidJUnit4.class) 90 public class DocumentsContractTest { 91 private static final String AUTHORITY = "com.example"; 92 93 private static final String DOC_RED = "red"; 94 private static final String DOC_GREEN = "green"; 95 private static final String DOC_BLUE = "blue"; 96 private static final String DOC_RESULT = "result"; 97 98 private static final Uri URI_RED = buildDocumentUri(AUTHORITY, DOC_RED); 99 private static final Uri URI_GREEN = buildDocumentUri(AUTHORITY, DOC_GREEN); 100 private static final Uri URI_BLUE = buildDocumentUri(AUTHORITY, DOC_BLUE); 101 private static final Uri URI_RESULT = buildDocumentUri(AUTHORITY, DOC_RESULT); 102 103 private static final String MIME_TYPE = "application/octet-stream"; 104 private static final String DISPLAY_NAME = "My Test"; 105 106 private Context mContext; 107 private DocumentsProvider mProvider; 108 private ContentResolver mResolver; 109 110 @Before setUp()111 public void setUp() { 112 mContext = mock(Context.class, RETURNS_DEFAULTS); 113 mProvider = mock(DocumentsProvider.class, CALLS_REAL_METHODS); 114 115 final ProviderInfo pi = new ProviderInfo(); 116 pi.authority = AUTHORITY; 117 pi.exported = true; 118 pi.grantUriPermissions = true; 119 pi.readPermission = android.Manifest.permission.MANAGE_DOCUMENTS; 120 pi.writePermission = android.Manifest.permission.MANAGE_DOCUMENTS; 121 mProvider.attachInfo(mContext, pi); 122 123 mResolver = ContentResolver.wrap(mProvider); 124 } 125 126 @Test testRootUri()127 public void testRootUri() { 128 final String auth = "com.example"; 129 final String rootId = "rootId"; 130 final PackageManager pm = mock(PackageManager.class); 131 final ProviderInfo providerInfo = new ProviderInfo(); 132 final ResolveInfo resolveInfo = new ResolveInfo(); 133 final List<ResolveInfo> infoList = new ArrayList<>(); 134 135 providerInfo.authority = auth; 136 resolveInfo.providerInfo = providerInfo; 137 infoList.add(resolveInfo); 138 139 doReturn(pm).when(mContext).getPackageManager(); 140 doReturn(infoList).when(pm).queryIntentContentProviders(any(Intent.class), anyInt()); 141 142 final Uri uri = DocumentsContract.buildRootUri(auth, rootId); 143 144 assertEquals(auth, uri.getAuthority()); 145 assertEquals(rootId, DocumentsContract.getRootId(uri)); 146 assertTrue(DocumentsContract.isRootUri(mContext, uri)); 147 } 148 149 @Test testRootUri_returnFalse()150 public void testRootUri_returnFalse() { 151 final String auth = "com.example"; 152 final String rootId = "rootId"; 153 final PackageManager pm = mock(PackageManager.class); 154 final List<ResolveInfo> infoList = new ArrayList<>(); 155 156 doReturn(pm).when(mContext).getPackageManager(); 157 doReturn(infoList).when(pm).queryIntentContentProviders(any(Intent.class), anyInt()); 158 159 final Uri uri = DocumentsContract.buildRootUri(auth, rootId); 160 161 assertEquals(auth, uri.getAuthority()); 162 assertEquals(rootId, DocumentsContract.getRootId(uri)); 163 assertFalse(DocumentsContract.isRootUri(mContext, uri)); 164 } 165 166 @Test testRootsUri()167 public void testRootsUri() { 168 final String auth = "com.example"; 169 final PackageManager pm = mock(PackageManager.class); 170 final ProviderInfo providerInfo = new ProviderInfo(); 171 final ResolveInfo resolveInfo = new ResolveInfo(); 172 final List<ResolveInfo> infoList = new ArrayList<>(); 173 174 providerInfo.authority = auth; 175 resolveInfo.providerInfo = providerInfo; 176 infoList.add(resolveInfo); 177 178 doReturn(pm).when(mContext).getPackageManager(); 179 doReturn(infoList).when(pm).queryIntentContentProviders(any(Intent.class), anyInt()); 180 181 final Uri uri = DocumentsContract.buildRootsUri(auth); 182 assertEquals(auth, uri.getAuthority()); 183 assertTrue(DocumentsContract.isRootsUri(mContext, uri)); 184 } 185 186 @Test testRootsUri_returnsFalse()187 public void testRootsUri_returnsFalse() { 188 final String auth = "com.example"; 189 final PackageManager pm = mock(PackageManager.class); 190 final List<ResolveInfo> infoList = new ArrayList<>(); 191 192 doReturn(pm).when(mContext).getPackageManager(); 193 doReturn(infoList).when(pm).queryIntentContentProviders(any(Intent.class), anyInt()); 194 195 final Uri uri = DocumentsContract.buildRootsUri(auth); 196 assertEquals(auth, uri.getAuthority()); 197 assertFalse(DocumentsContract.isRootsUri(mContext, uri)); 198 } 199 200 @Test testDocumentUri()201 public void testDocumentUri() { 202 final String auth = "com.example"; 203 final String docId = "doc:12"; 204 205 final Uri uri = DocumentsContract.buildDocumentUri(auth, docId); 206 assertEquals(auth, uri.getAuthority()); 207 assertEquals(docId, DocumentsContract.getDocumentId(uri)); 208 assertFalse(DocumentsContract.isTreeUri(uri)); 209 } 210 211 @Test testTreeDocumentUri()212 public void testTreeDocumentUri() { 213 final String auth = "com.example"; 214 final String treeId = "doc:12"; 215 final String leafId = "doc:24"; 216 217 final Uri treeUri = DocumentsContract.buildTreeDocumentUri(auth, treeId); 218 assertEquals(auth, treeUri.getAuthority()); 219 assertEquals(treeId, DocumentsContract.getTreeDocumentId(treeUri)); 220 assertTrue(DocumentsContract.isTreeUri(treeUri)); 221 222 final Uri leafUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, leafId); 223 assertEquals(auth, leafUri.getAuthority()); 224 assertEquals(treeId, DocumentsContract.getTreeDocumentId(leafUri)); 225 assertEquals(leafId, DocumentsContract.getDocumentId(leafUri)); 226 assertTrue(DocumentsContract.isTreeUri(leafUri)); 227 } 228 229 @Test testCreateDocument()230 public void testCreateDocument() throws Exception { 231 doReturn(DOC_RESULT).when(mProvider).createDocument(DOC_RED, MIME_TYPE, DISPLAY_NAME); 232 assertEquals(URI_RESULT, createDocument(mResolver, URI_RED, MIME_TYPE, DISPLAY_NAME)); 233 } 234 235 @Test testRenameDocument()236 public void testRenameDocument() throws Exception { 237 doReturn(DOC_RESULT).when(mProvider).renameDocument(DOC_RED, DISPLAY_NAME); 238 assertEquals(URI_RESULT, renameDocument(mResolver, URI_RED, DISPLAY_NAME)); 239 } 240 241 @Test testDeleteDocument()242 public void testDeleteDocument() throws Exception { 243 doNothing().when(mProvider).deleteDocument(DOC_RED); 244 assertEquals(true, deleteDocument(mResolver, URI_RED)); 245 } 246 247 @Test testDeleteDocument_Failure()248 public void testDeleteDocument_Failure() throws Exception { 249 doThrow(new RuntimeException()).when(mProvider).deleteDocument(DOC_RED); 250 try { 251 deleteDocument(mResolver, URI_RED); 252 fail(); 253 } catch (RuntimeException expected) { 254 } 255 } 256 257 @Test testCopyDocument()258 public void testCopyDocument() throws Exception { 259 doReturn(DOC_RESULT).when(mProvider).copyDocument(DOC_RED, DOC_GREEN); 260 assertEquals(URI_RESULT, copyDocument(mResolver, URI_RED, URI_GREEN)); 261 } 262 263 @Test testMoveDocument()264 public void testMoveDocument() throws Exception { 265 doReturn(DOC_RESULT).when(mProvider).moveDocument(DOC_RED, DOC_GREEN, DOC_BLUE); 266 assertEquals(URI_RESULT, moveDocument(mResolver, URI_RED, URI_GREEN, URI_BLUE)); 267 } 268 269 @Test testIsChildDocument()270 public void testIsChildDocument() throws Exception { 271 doReturn(true).when(mProvider).isChildDocument(DOC_RED, DOC_GREEN); 272 assertEquals(true, isChildDocument(mResolver, URI_RED, URI_GREEN)); 273 } 274 275 @Test testIsChildDocument_Failure()276 public void testIsChildDocument_Failure() throws Exception { 277 doReturn(false).when(mProvider).isChildDocument(DOC_RED, DOC_GREEN); 278 assertEquals(false, isChildDocument(mResolver, URI_RED, URI_GREEN)); 279 } 280 281 @Test testRemoveDocument()282 public void testRemoveDocument() throws Exception { 283 doNothing().when(mProvider).removeDocument(DOC_RED, DOC_GREEN); 284 assertEquals(true, removeDocument(mResolver, URI_RED, URI_GREEN)); 285 } 286 287 @Test testRemoveDocument_Failure()288 public void testRemoveDocument_Failure() throws Exception { 289 doThrow(new RuntimeException()).when(mProvider).removeDocument(DOC_RED, DOC_GREEN); 290 try { 291 removeDocument(mResolver, URI_RED, URI_GREEN); 292 fail(); 293 } catch (RuntimeException expected) { 294 } 295 } 296 297 @Test testEjectRoot()298 public void testEjectRoot() throws Exception { 299 doNothing().when(mProvider).ejectRoot("r00t"); 300 ejectRoot(mResolver, buildRootUri(AUTHORITY, "r00t")); 301 } 302 303 @Test testEjectRoot_Failure()304 public void testEjectRoot_Failure() throws Exception { 305 doThrow(new RuntimeException()).when(mProvider).ejectRoot("r00t"); 306 ejectRoot(mResolver, buildRootUri(AUTHORITY, "r00t")); 307 } 308 309 @Test testFindDocumentPath()310 public void testFindDocumentPath() throws Exception { 311 final Path res = new Path(null, Arrays.asList("red", "blue")); 312 313 doReturn(true).when(mProvider).isChildDocument(DOC_RED, DOC_BLUE); 314 doReturn(res).when(mProvider).findDocumentPath(DOC_RED, DOC_BLUE); 315 assertEquals(res, findDocumentPath(mResolver, 316 buildDocumentUriUsingTree(buildTreeDocumentUri(AUTHORITY, DOC_RED), DOC_BLUE))); 317 } 318 319 @Test testCreateWebLinkIntent()320 public void testCreateWebLinkIntent() throws Exception { 321 final IntentSender res = PendingIntent 322 .getActivity(InstrumentationRegistry.getTargetContext(), 0, 323 new Intent(Intent.ACTION_VIEW), 0) 324 .getIntentSender(); 325 326 doReturn(res).when(mProvider).createWebLinkIntent(DOC_RED, Bundle.EMPTY); 327 assertEquals(res, createWebLinkIntent(mResolver, URI_RED, Bundle.EMPTY)); 328 } 329 330 @Test testGetDocumentMetadata()331 public void testGetDocumentMetadata() throws Exception { 332 doReturn(Bundle.EMPTY).when(mProvider).getDocumentMetadata(DOC_RED); 333 assertEquals(Bundle.EMPTY, getDocumentMetadata(mResolver, URI_RED)); 334 } 335 336 @Test testGetDocumentStreamTypes()337 public void testGetDocumentStreamTypes() throws Exception { 338 final String[] res = new String[] { MIME_TYPE }; 339 340 doReturn(res).when(mProvider).getDocumentStreamTypes(DOC_RED, MIME_TYPE); 341 assertArrayEquals(res, mResolver.getStreamTypes(URI_RED, MIME_TYPE)); 342 } 343 344 @Test testGetDocumentType()345 public void testGetDocumentType() throws Exception { 346 doReturn(MIME_TYPE).when(mProvider).getDocumentType(DOC_RED); 347 assertEquals(MIME_TYPE, mResolver.getType(URI_RED)); 348 } 349 350 @Test testOpenDocument()351 public void testOpenDocument() throws Exception { 352 final ParcelFileDescriptor res = ParcelFileDescriptor.open(new File("/dev/null"), 353 ParcelFileDescriptor.MODE_READ_ONLY); 354 355 doReturn(res).when(mProvider).openDocument(DOC_RED, "r", null); 356 assertEquals(res, mResolver.openFile(URI_RED, "r", null)); 357 } 358 359 @Test testOpenDocumentThumbnail()360 public void testOpenDocumentThumbnail() throws Exception { 361 final AssetFileDescriptor res = new AssetFileDescriptor( 362 ParcelFileDescriptor.open(new File("/dev/null"), 363 ParcelFileDescriptor.MODE_READ_ONLY), 364 0, AssetFileDescriptor.UNKNOWN_LENGTH); 365 366 final Point size = new Point(32, 32); 367 final Bundle opts = new Bundle(); 368 opts.putParcelable(ContentResolver.EXTRA_SIZE, size); 369 370 doReturn(res).when(mProvider).openDocumentThumbnail(DOC_RED, size, null); 371 assertEquals(res, mResolver.openTypedAssetFile(URI_RED, MIME_TYPE, opts, null)); 372 } 373 374 @Test testOpenTypedDocument()375 public void testOpenTypedDocument() throws Exception { 376 final AssetFileDescriptor res = new AssetFileDescriptor( 377 ParcelFileDescriptor.open(new File("/dev/null"), 378 ParcelFileDescriptor.MODE_READ_ONLY), 379 0, AssetFileDescriptor.UNKNOWN_LENGTH); 380 381 doReturn("image/png").when(mProvider).getDocumentType(DOC_RED); 382 doReturn(res).when(mProvider).openTypedDocument(DOC_RED, "audio/*", null, null); 383 assertEquals(res, mResolver.openTypedAssetFile(URI_RED, "audio/*", null, null)); 384 } 385 386 @Test testQueryDocument()387 public void testQueryDocument() throws Exception { 388 final Cursor res = new MatrixCursor(new String[0]); 389 390 doReturn(res).when(mProvider).queryDocument(DOC_RED, null); 391 assertEquals(res, mResolver.query(URI_RED, null, null, null)); 392 } 393 394 @Test testQueryRoots()395 public void testQueryRoots() throws Exception { 396 final Cursor res = new MatrixCursor(new String[0]); 397 398 doReturn(res).when(mProvider).queryRoots(null); 399 assertEquals(res, mResolver.query(buildRootsUri(AUTHORITY), null, null, null)); 400 } 401 402 @Test testQueryChildDocuments()403 public void testQueryChildDocuments() throws Exception { 404 final Cursor res = new MatrixCursor(new String[0]); 405 406 doReturn(res).when(mProvider).queryChildDocuments(DOC_RED, null, Bundle.EMPTY); 407 assertEquals(res, mResolver.query(buildChildDocumentsUri(AUTHORITY, DOC_RED), null, 408 Bundle.EMPTY, null)); 409 } 410 411 @Test testQueryRecentDocuments()412 public void testQueryRecentDocuments() throws Exception { 413 final Cursor res = new MatrixCursor(new String[0]); 414 415 doReturn(res).when(mProvider).queryRecentDocuments(DOC_RED, null, Bundle.EMPTY, null); 416 assertEquals(res, mResolver.query(buildRecentDocumentsUri(AUTHORITY, DOC_RED), null, 417 Bundle.EMPTY, null)); 418 } 419 420 @Test testQuerySearchDocuments()421 public void testQuerySearchDocuments() throws Exception { 422 final Cursor res = new MatrixCursor(new String[0]); 423 424 doReturn(res).when(mProvider).querySearchDocuments(DOC_RED, null, Bundle.EMPTY); 425 assertEquals(res, mResolver.query(buildSearchDocumentsUri(AUTHORITY, DOC_RED, "moo"), null, 426 Bundle.EMPTY, null)); 427 } 428 429 @Test testGetDocumentThumbnail()430 public void testGetDocumentThumbnail() throws Exception { 431 // create file and image 432 final String testImagePath = 433 InstrumentationRegistry.getTargetContext().getExternalCacheDir().getPath() 434 + "/testimage.jpg"; 435 final int imageSize = 128; 436 final int thumbnailSize = 32; 437 File file = new File(testImagePath); 438 try (FileOutputStream out = new FileOutputStream(file)) { 439 writeImage(imageSize, imageSize, Color.RED, out); 440 } 441 442 final AssetFileDescriptor res = new AssetFileDescriptor( 443 ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY), 444 0, AssetFileDescriptor.UNKNOWN_LENGTH); 445 final Point size = new Point(thumbnailSize, thumbnailSize); 446 final Bundle opts = new Bundle(); 447 opts.putParcelable(ContentResolver.EXTRA_SIZE, size); 448 449 doReturn(res).when(mProvider).openDocumentThumbnail(DOC_RED, size, null); 450 Bitmap bitmap = DocumentsContract.getDocumentThumbnail(mResolver, URI_RED, size, null); 451 452 // A provider may return a thumbnail of a different size, but never more than double the 453 // requested size. 454 assertFalse(bitmap.getWidth() > thumbnailSize * 2); 455 assertFalse(bitmap.getHeight() > thumbnailSize * 2); 456 assertColorMostlyEquals(Color.RED, 457 bitmap.getPixel(bitmap.getWidth() / 2, bitmap.getHeight() / 2)); 458 459 // clean up 460 file.delete(); 461 bitmap.recycle(); 462 } 463 writeImage(int width, int height, int color, OutputStream out)464 private static void writeImage(int width, int height, int color, OutputStream out) { 465 final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 466 final Canvas canvas = new Canvas(bitmap); 467 canvas.drawColor(color); 468 bitmap.compress(Bitmap.CompressFormat.PNG, 90, out); 469 } 470 471 /** 472 * Since thumbnails might be bounced through a compression pass, we're okay 473 * if they're mostly equal. 474 */ assertColorMostlyEquals(int expected, int actual)475 private static void assertColorMostlyEquals(int expected, int actual) { 476 assertEquals(Integer.toHexString(expected & 0xF0F0F0F0), 477 Integer.toHexString(actual & 0xF0F0F0F0)); 478 } 479 } 480