• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.mtp;
18 
19 import android.content.Context;
20 import android.database.Cursor;
21 import android.mtp.MtpObjectInfo;
22 import android.net.Uri;
23 import android.provider.DocumentsContract;
24 import android.provider.DocumentsContract.Document;
25 import android.test.AndroidTestCase;
26 import android.test.suitebuilder.annotation.MediumTest;
27 
28 import java.io.IOException;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.concurrent.CountDownLatch;
32 import java.util.concurrent.TimeoutException;
33 
34 @MediumTest
35 public class DocumentLoaderTest extends AndroidTestCase {
36     private MtpDatabase mDatabase;
37     private BlockableTestMtpManager mManager;
38     private TestContentResolver mResolver;
39     private DocumentLoader mLoader;
40     final private Identifier mParentIdentifier = new Identifier(
41             0, 0, 0, "2", MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE);
42 
43     @Override
setUp()44     public void setUp() throws Exception {
45         mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
46 
47         mDatabase.getMapper().startAddingDocuments(null);
48         mDatabase.getMapper().putDeviceDocument(
49                 new MtpDeviceRecord(0, "Device", null, true, new MtpRoot[0], null, null));
50         mDatabase.getMapper().stopAddingDocuments(null);
51 
52         mDatabase.getMapper().startAddingDocuments("1");
53         mDatabase.getMapper().putStorageDocuments("1", new int[0], new MtpRoot[] {
54                 new MtpRoot(0, 0, "Storage", 1000, 1000, "")
55         });
56         mDatabase.getMapper().stopAddingDocuments("1");
57 
58         mManager = new BlockableTestMtpManager(getContext());
59         mResolver = new TestContentResolver();
60     }
61 
62     @Override
tearDown()63     public void tearDown() throws Exception {
64         mLoader.close();
65         mDatabase.close();
66     }
67 
testBasic()68     public void testBasic() throws Exception {
69         setUpLoader();
70 
71         final Uri uri = DocumentsContract.buildChildDocumentsUri(
72                 MtpDocumentsProvider.AUTHORITY, mParentIdentifier.mDocumentId);
73         setUpDocument(mManager, 40);
74         mManager.blockDocument(0, 15);
75         mManager.blockDocument(0, 35);
76 
77         {
78             final Cursor cursor = mLoader.queryChildDocuments(
79                     MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier);
80             assertEquals(DocumentLoader.NUM_INITIAL_ENTRIES, cursor.getCount());
81         }
82 
83         Thread.sleep(DocumentLoader.NOTIFY_PERIOD_MS);
84         mManager.unblockDocument(0, 15);
85         mResolver.waitForNotification(uri, 1);
86 
87         {
88             final Cursor cursor = mLoader.queryChildDocuments(
89                     MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier);
90             assertEquals(
91                     DocumentLoader.NUM_INITIAL_ENTRIES + DocumentLoader.NUM_LOADING_ENTRIES,
92                     cursor.getCount());
93         }
94 
95         mManager.unblockDocument(0, 35);
96         mResolver.waitForNotification(uri, 2);
97 
98         {
99             final Cursor cursor = mLoader.queryChildDocuments(
100                     MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier);
101             assertEquals(40, cursor.getCount());
102         }
103 
104         assertEquals(2, mResolver.getChangeCount(uri));
105     }
106 
testError_GetObjectHandles()107     public void testError_GetObjectHandles() throws Exception {
108         mManager = new BlockableTestMtpManager(getContext()) {
109             @Override
110             int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
111                     throws IOException {
112                 throw new IOException();
113             }
114         };
115         setUpLoader();
116         mManager.setObjectHandles(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, null);
117         try {
118             try (final Cursor cursor = mLoader.queryChildDocuments(
119                     MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {}
120             fail();
121         } catch (IOException exception) {
122             // Expect exception.
123         }
124     }
125 
testError_GetObjectInfo()126     public void testError_GetObjectInfo() throws Exception {
127         mManager = new BlockableTestMtpManager(getContext()) {
128             @Override
129             MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
130                 if (objectHandle == DocumentLoader.NUM_INITIAL_ENTRIES) {
131                     throw new IOException();
132                 } else {
133                     return super.getObjectInfo(deviceId, objectHandle);
134                 }
135             }
136         };
137         setUpLoader();
138         setUpDocument(mManager, DocumentLoader.NUM_INITIAL_ENTRIES);
139         try (final Cursor cursor = mLoader.queryChildDocuments(
140                 MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {
141             // Even if MtpManager returns an error for a document, loading must complete.
142             assertFalse(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
143         }
144     }
145 
testCancelTask()146     public void testCancelTask() throws IOException, InterruptedException, TimeoutException {
147         setUpDocument(mManager,
148                 DocumentLoader.NUM_INITIAL_ENTRIES + 1);
149 
150         // Block the first iteration in the background thread.
151         mManager.blockDocument(
152                 0, DocumentLoader.NUM_INITIAL_ENTRIES + 1);
153         setUpLoader();
154         try (final Cursor cursor = mLoader.queryChildDocuments(
155                 MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {
156             assertTrue(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
157         }
158 
159         final Uri uri = DocumentsContract.buildChildDocumentsUri(
160                 MtpDocumentsProvider.AUTHORITY, mParentIdentifier.mDocumentId);
161         assertEquals(0, mResolver.getChangeCount(uri));
162 
163         // Clear task while the first iteration is being blocked.
164         mLoader.cancelTask(mParentIdentifier);
165         mManager.unblockDocument(
166                 0, DocumentLoader.NUM_INITIAL_ENTRIES + 1);
167         Thread.sleep(DocumentLoader.NOTIFY_PERIOD_MS);
168         assertEquals(0, mResolver.getChangeCount(uri));
169 
170         // Check if it's OK to query invalidated task.
171         try (final Cursor cursor = mLoader.queryChildDocuments(
172                 MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {
173             assertTrue(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
174         }
175         mResolver.waitForNotification(uri, 1);
176     }
177 
setUpLoader()178     private void setUpLoader() {
179         mLoader = new DocumentLoader(
180                 new MtpDeviceRecord(
181                         0, "Device", "Key", true, new MtpRoot[0],
182                         TestUtil.OPERATIONS_SUPPORTED, new int[0]),
183                 mManager,
184                 mResolver,
185                 mDatabase);
186     }
187 
setUpDocument(TestMtpManager manager, int count)188     private void setUpDocument(TestMtpManager manager, int count) {
189         int[] childDocuments = new int[count];
190         for (int i = 0; i < childDocuments.length; i++) {
191             final int objectHandle = i + 1;
192             childDocuments[i] = objectHandle;
193             manager.setObjectInfo(0, new MtpObjectInfo.Builder()
194                     .setObjectHandle(objectHandle)
195                     .setName(Integer.toString(i))
196                     .build());
197         }
198         manager.setObjectHandles(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, childDocuments);
199     }
200 
201     private static class BlockableTestMtpManager extends TestMtpManager {
202         final private Map<String, CountDownLatch> blockedDocuments = new HashMap<>();
203 
BlockableTestMtpManager(Context context)204         BlockableTestMtpManager(Context context) {
205             super(context);
206         }
207 
blockDocument(int deviceId, int objectHandle)208         void blockDocument(int deviceId, int objectHandle) {
209             blockedDocuments.put(pack(deviceId, objectHandle), new CountDownLatch(1));
210         }
211 
unblockDocument(int deviceId, int objectHandle)212         void unblockDocument(int deviceId, int objectHandle) {
213             blockedDocuments.get(pack(deviceId, objectHandle)).countDown();
214         }
215 
216         @Override
getObjectInfo(int deviceId, int objectHandle)217         MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
218             final CountDownLatch latch = blockedDocuments.get(pack(deviceId, objectHandle));
219             if (latch != null) {
220                 try {
221                     latch.await();
222                 } catch(InterruptedException e) {
223                     fail();
224                 }
225             }
226             return super.getObjectInfo(deviceId, objectHandle);
227         }
228     }
229 }
230