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