MtpDocumentsProviderTest.java revision 81d48536aef702d301cdc3a339008de767b51f99
1/* 2 * Copyright (C) 2015 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 17package com.android.mtp; 18 19import android.database.Cursor; 20import android.mtp.MtpConstants; 21import android.mtp.MtpObjectInfo; 22import android.net.Uri; 23import android.provider.DocumentsContract.Root; 24import android.provider.DocumentsContract; 25import android.test.AndroidTestCase; 26import android.test.suitebuilder.annotation.SmallTest; 27 28import java.io.FileNotFoundException; 29import java.io.IOException; 30import java.util.concurrent.TimeoutException; 31 32import static com.android.mtp.MtpDatabase.strings; 33 34@SmallTest 35public class MtpDocumentsProviderTest extends AndroidTestCase { 36 private final static Uri ROOTS_URI = 37 DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY); 38 private TestContentResolver mResolver; 39 private MtpDocumentsProvider mProvider; 40 private TestMtpManager mMtpManager; 41 private final TestResources mResources = new TestResources(); 42 private MtpDatabase mDatabase; 43 44 @Override 45 public void setUp() throws IOException { 46 mResolver = new TestContentResolver(); 47 mMtpManager = new TestMtpManager(getContext()); 48 } 49 50 @Override 51 public void tearDown() { 52 mProvider.shutdown(); 53 MtpDatabase.deleteDatabase(getContext()); 54 } 55 56 public void testOpenAndCloseDevice() throws Exception { 57 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 58 mMtpManager.addValidDevice(new MtpDeviceRecord( 59 0, 60 "Device", 61 false /* unopened */, 62 new MtpRoot[] { 63 new MtpRoot( 64 0 /* deviceId */, 65 1 /* storageId */, 66 "Device A" /* device model name */, 67 "Storage A" /* volume description */, 68 1024 /* free space */, 69 2048 /* total space */, 70 "" /* no volume identifier */) 71 })); 72 73 mProvider.openDevice(0); 74 mResolver.waitForNotification(ROOTS_URI, 1); 75 76 mProvider.closeDevice(0); 77 mResolver.waitForNotification(ROOTS_URI, 2); 78 } 79 80 public void testOpenAndCloseErrorDevice() throws Exception { 81 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 82 try { 83 mProvider.openDevice(1); 84 fail(); 85 } catch (Throwable error) { 86 assertTrue(error instanceof IOException); 87 } 88 89 try { 90 mProvider.closeDevice(1); 91 fail(); 92 } catch (Throwable error) { 93 assertTrue(error instanceof IOException); 94 } 95 96 // Check if the following notification is the first one or not. 97 mMtpManager.addValidDevice(new MtpDeviceRecord( 98 0, 99 "Device", 100 false /* unopened */, 101 new MtpRoot[] { 102 new MtpRoot( 103 0 /* deviceId */, 104 1 /* storageId */, 105 "Device A" /* device model name */, 106 "Storage A" /* volume description */, 107 1024 /* free space */, 108 2048 /* total space */, 109 "" /* no volume identifier */) 110 })); 111 mProvider.openDevice(0); 112 mResolver.waitForNotification(ROOTS_URI, 1); 113 } 114 115 public void testQueryRoots() throws Exception { 116 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 117 mMtpManager.addValidDevice(new MtpDeviceRecord( 118 0, 119 "Device", 120 false /* unopened */, 121 new MtpRoot[] { 122 new MtpRoot( 123 0 /* deviceId */, 124 1 /* storageId */, 125 "Device A" /* device model name */, 126 "Storage A" /* volume description */, 127 1024 /* free space */, 128 2048 /* total space */, 129 "" /* no volume identifier */) 130 })); 131 mMtpManager.addValidDevice(new MtpDeviceRecord( 132 1, 133 "Device", 134 false /* unopened */, 135 new MtpRoot[] { 136 new MtpRoot( 137 1 /* deviceId */, 138 1 /* storageId */, 139 "Device B" /* device model name */, 140 "Storage B" /* volume description */, 141 2048 /* free space */, 142 4096 /* total space */, 143 "Identifier B" /* no volume identifier */) 144 })); 145 146 { 147 mProvider.openDevice(0); 148 mResolver.waitForNotification(ROOTS_URI, 1); 149 final Cursor cursor = mProvider.queryRoots(null); 150 assertEquals(2, cursor.getCount()); 151 cursor.moveToNext(); 152 assertEquals("3", cursor.getString(0)); 153 assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); 154 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); 155 assertEquals("Device A Storage A", cursor.getString(3)); 156 assertEquals("3", cursor.getString(4)); 157 assertEquals(1024, cursor.getInt(5)); 158 } 159 160 { 161 mProvider.openDevice(1); 162 mResolver.waitForNotification(ROOTS_URI, 2); 163 final Cursor cursor = mProvider.queryRoots(null); 164 assertEquals(2, cursor.getCount()); 165 cursor.moveToNext(); 166 cursor.moveToNext(); 167 assertEquals("4", cursor.getString(0)); 168 assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); 169 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); 170 assertEquals("Device B Storage B", cursor.getString(3)); 171 assertEquals("4", cursor.getString(4)); 172 assertEquals(2048, cursor.getInt(5)); 173 } 174 } 175 176 public void testQueryRoots_error() throws Exception { 177 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 178 mMtpManager.addValidDevice( 179 new MtpDeviceRecord(0, "Device A", false /* unopened */, new MtpRoot[0])); 180 mMtpManager.addValidDevice(new MtpDeviceRecord( 181 1, 182 "Device", 183 false /* unopened */, 184 new MtpRoot[] { 185 new MtpRoot( 186 1 /* deviceId */, 187 1 /* storageId */, 188 "Device B" /* device model name */, 189 "Storage B" /* volume description */, 190 2048 /* free space */, 191 4096 /* total space */, 192 "Identifier B" /* no volume identifier */) 193 })); 194 { 195 mProvider.openDevice(0); 196 mProvider.openDevice(1); 197 mResolver.waitForNotification(ROOTS_URI, 1); 198 199 final Cursor cursor = mProvider.queryRoots(null); 200 assertEquals(2, cursor.getCount()); 201 202 cursor.moveToNext(); 203 assertEquals("1", cursor.getString(0)); 204 assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); 205 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); 206 assertEquals("Device A", cursor.getString(3)); 207 assertEquals("1", cursor.getString(4)); 208 assertEquals(0, cursor.getInt(5)); 209 210 cursor.moveToNext(); 211 assertEquals("3", cursor.getString(0)); 212 assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); 213 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); 214 assertEquals("Device B Storage B", cursor.getString(3)); 215 assertEquals("3", cursor.getString(4)); 216 assertEquals(2048, cursor.getInt(5)); 217 } 218 } 219 220 public void testQueryDocument() throws IOException, InterruptedException, TimeoutException { 221 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 222 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") }); 223 setupDocuments( 224 0, 225 0, 226 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, 227 "1", 228 new MtpObjectInfo[] { 229 new MtpObjectInfo.Builder() 230 .setObjectHandle(100) 231 .setFormat(MtpConstants.FORMAT_EXIF_JPEG) 232 .setName("image.jpg") 233 .setDateModified(1422716400000L) 234 .setCompressedSize(1024 * 1024 * 5) 235 .setThumbCompressedSize(50 * 1024) 236 .build() 237 }); 238 239 final Cursor cursor = mProvider.queryDocument("3", null); 240 assertEquals(1, cursor.getCount()); 241 242 cursor.moveToNext(); 243 244 assertEquals("3", cursor.getString(0)); 245 assertEquals("image/jpeg", cursor.getString(1)); 246 assertEquals("image.jpg", cursor.getString(2)); 247 assertEquals(1422716400000L, cursor.getLong(3)); 248 assertEquals( 249 DocumentsContract.Document.FLAG_SUPPORTS_DELETE | 250 DocumentsContract.Document.FLAG_SUPPORTS_WRITE | 251 DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL, 252 cursor.getInt(4)); 253 assertEquals(1024 * 1024 * 5, cursor.getInt(5)); 254 } 255 256 public void testQueryDocument_directory() 257 throws IOException, InterruptedException, TimeoutException { 258 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 259 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") }); 260 setupDocuments( 261 0, 262 0, 263 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, 264 "1", 265 new MtpObjectInfo[] { 266 new MtpObjectInfo.Builder() 267 .setObjectHandle(2) 268 .setStorageId(1) 269 .setFormat(MtpConstants.FORMAT_ASSOCIATION) 270 .setName("directory") 271 .setDateModified(1422716400000L) 272 .build() 273 }); 274 275 final Cursor cursor = mProvider.queryDocument("3", null); 276 assertEquals(1, cursor.getCount()); 277 278 cursor.moveToNext(); 279 assertEquals("3", cursor.getString(0)); 280 assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1)); 281 assertEquals("directory", cursor.getString(2)); 282 assertEquals(1422716400000L, cursor.getLong(3)); 283 assertEquals( 284 DocumentsContract.Document.FLAG_SUPPORTS_DELETE | 285 DocumentsContract.Document.FLAG_SUPPORTS_WRITE | 286 DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE, 287 cursor.getInt(4)); 288 assertEquals(0, cursor.getInt(5)); 289 } 290 291 public void testQueryDocument_forRoot() 292 throws IOException, InterruptedException, TimeoutException { 293 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 294 setupRoots(0, new MtpRoot[] { 295 new MtpRoot( 296 0 /* deviceId */, 297 1 /* storageId */, 298 "Device A" /* device model name */, 299 "Storage A" /* volume description */, 300 1024 /* free space */, 301 4096 /* total space */, 302 "" /* no volume identifier */) 303 }); 304 final Cursor cursor = mProvider.queryDocument("2", null); 305 assertEquals(1, cursor.getCount()); 306 307 cursor.moveToNext(); 308 assertEquals("2", cursor.getString(0)); 309 assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1)); 310 assertEquals("Device A Storage A", cursor.getString(2)); 311 assertTrue(cursor.isNull(3)); 312 assertEquals(0, cursor.getInt(4)); 313 assertEquals(3072, cursor.getInt(5)); 314 } 315 316 public void testQueryChildDocuments() throws Exception { 317 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 318 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") }); 319 setupDocuments( 320 0, 321 0, 322 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, 323 "1", 324 new MtpObjectInfo[] { 325 new MtpObjectInfo.Builder() 326 .setObjectHandle(100) 327 .setFormat(MtpConstants.FORMAT_EXIF_JPEG) 328 .setName("image.jpg") 329 .setCompressedSize(1024 * 1024 * 5) 330 .setThumbCompressedSize(5 * 1024) 331 .setProtectionStatus(MtpConstants.PROTECTION_STATUS_READ_ONLY) 332 .build() 333 }); 334 335 final Cursor cursor = mProvider.queryChildDocuments("1", null, null); 336 assertEquals(1, cursor.getCount()); 337 338 assertTrue(cursor.moveToNext()); 339 assertEquals("3", cursor.getString(0)); 340 assertEquals("image/jpeg", cursor.getString(1)); 341 assertEquals("image.jpg", cursor.getString(2)); 342 assertEquals(0, cursor.getLong(3)); 343 assertEquals(DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL, cursor.getInt(4)); 344 assertEquals(1024 * 1024 * 5, cursor.getInt(5)); 345 346 cursor.close(); 347 } 348 349 public void testQueryChildDocuments_cursorError() throws Exception { 350 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 351 try { 352 mProvider.queryChildDocuments("1", null, null); 353 fail(); 354 } catch (Throwable error) { 355 assertTrue(error instanceof FileNotFoundException); 356 } 357 } 358 359 public void testQueryChildDocuments_documentError() throws Exception { 360 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 361 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 1000, 1000, "") }); 362 mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 }); 363 try { 364 mProvider.queryChildDocuments("1", null, null); 365 fail(); 366 } catch (Throwable error) { 367 assertTrue(error instanceof FileNotFoundException); 368 } 369 } 370 371 public void testDeleteDocument() throws IOException, InterruptedException, TimeoutException { 372 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 373 setupRoots(0, new MtpRoot[] { 374 new MtpRoot(0, 0, "Device", "Storage", 0, 0, "") 375 }); 376 setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] { 377 new MtpObjectInfo.Builder() 378 .setName("test.txt") 379 .setObjectHandle(1) 380 .setParent(-1) 381 .build() 382 }); 383 384 mProvider.deleteDocument("3"); 385 assertEquals(1, mResolver.getChangeCount( 386 DocumentsContract.buildChildDocumentsUri( 387 MtpDocumentsProvider.AUTHORITY, "1"))); 388 } 389 390 public void testDeleteDocument_error() 391 throws IOException, InterruptedException, TimeoutException { 392 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); 393 setupRoots(0, new MtpRoot[] { 394 new MtpRoot(0, 0, "Device", "Storage", 0, 0, "") 395 }); 396 setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] { 397 new MtpObjectInfo.Builder() 398 .setName("test.txt") 399 .setObjectHandle(1) 400 .setParent(-1) 401 .build() 402 }); 403 try { 404 mProvider.deleteDocument("4"); 405 fail(); 406 } catch (Throwable e) { 407 assertTrue(e instanceof IOException); 408 } 409 assertEquals(0, mResolver.getChangeCount( 410 DocumentsContract.buildChildDocumentsUri( 411 MtpDocumentsProvider.AUTHORITY, "1"))); 412 } 413 414 private void setupProvider(int flag) { 415 mDatabase = new MtpDatabase(getContext(), flag); 416 mProvider = new MtpDocumentsProvider(); 417 mProvider.onCreateForTesting(mResources, mMtpManager, mResolver, mDatabase); 418 } 419 420 private String[] getStrings(Cursor cursor) { 421 try { 422 final String[] results = new String[cursor.getCount()]; 423 for (int i = 0; cursor.moveToNext(); i++) { 424 results[i] = cursor.getString(0); 425 } 426 return results; 427 } finally { 428 cursor.close(); 429 } 430 } 431 432 private String[] setupRoots(int deviceId, MtpRoot[] roots) 433 throws InterruptedException, TimeoutException, IOException { 434 final int changeCount = mResolver.getChangeCount(ROOTS_URI); 435 mMtpManager.addValidDevice( 436 new MtpDeviceRecord(deviceId, "Device", false /* unopened */, roots)); 437 mProvider.openDevice(deviceId); 438 mResolver.waitForNotification(ROOTS_URI, changeCount + 1); 439 return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID))); 440 } 441 442 private String[] setupDocuments( 443 int deviceId, 444 int storageId, 445 int parentHandle, 446 String parentDocumentId, 447 MtpObjectInfo[] objects) throws FileNotFoundException { 448 final int[] handles = new int[objects.length]; 449 int i = 0; 450 for (final MtpObjectInfo info : objects) { 451 handles[i] = info.getObjectHandle(); 452 mMtpManager.setObjectInfo(deviceId, info); 453 } 454 mMtpManager.setObjectHandles(deviceId, storageId, parentHandle, handles); 455 return getStrings(mProvider.queryChildDocuments( 456 parentDocumentId, strings(DocumentsContract.Document.COLUMN_DOCUMENT_ID), null)); 457 } 458} 459