blob: 884d132d2b42740132133f90523b810bff096b86 [file] [log] [blame]
Daichi Hirono9a05d6b2015-07-09 15:36:42 +09001/*
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
Daichi Hirono09109562015-07-28 20:57:05 +090019import android.database.Cursor;
Tomasz Mikolajewskibb430fa2015-08-25 18:34:30 +090020import android.mtp.MtpConstants;
Tomasz Mikolajewskibb430fa2015-08-25 18:34:30 +090021import android.mtp.MtpObjectInfo;
Daichi Hironod5152422015-07-15 13:31:51 +090022import android.net.Uri;
Daichi Hironob36b1552016-01-25 14:26:14 +090023import android.os.ParcelFileDescriptor;
24import android.os.storage.StorageManager;
Daichi Hirono4e94b8d2016-02-21 22:42:41 +090025import android.provider.DocumentsContract.Document;
Daichi Hirono09109562015-07-28 20:57:05 +090026import android.provider.DocumentsContract.Root;
Daichi Hironob36b1552016-01-25 14:26:14 +090027import android.system.Os;
28import android.system.OsConstants;
Tomasz Mikolajewskibb430fa2015-08-25 18:34:30 +090029import android.provider.DocumentsContract;
Daichi Hirono9a05d6b2015-07-09 15:36:42 +090030import android.test.AndroidTestCase;
Daichi Hironob36b1552016-01-25 14:26:14 +090031import android.test.suitebuilder.annotation.MediumTest;
Daichi Hirono9a05d6b2015-07-09 15:36:42 +090032
Daichi Hironoc18f8072016-02-10 14:59:52 -080033import com.android.mtp.exceptions.BusyDeviceException;
34
Daichi Hirono5fecc6c2015-08-04 17:45:51 +090035import java.io.FileNotFoundException;
Daichi Hironod5152422015-07-15 13:31:51 +090036import java.io.IOException;
Daichi Hironob36b1552016-01-25 14:26:14 +090037import java.util.Arrays;
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +090038import java.util.concurrent.TimeoutException;
39
Daichi Hirono259ce802015-11-20 17:51:53 +090040import static com.android.mtp.MtpDatabase.strings;
Daichi Hironod5152422015-07-15 13:31:51 +090041
Daichi Hironob36b1552016-01-25 14:26:14 +090042@MediumTest
Daichi Hirono9a05d6b2015-07-09 15:36:42 +090043public class MtpDocumentsProviderTest extends AndroidTestCase {
Daichi Hirono8b9024f2015-08-12 12:59:09 +090044 private final static Uri ROOTS_URI =
45 DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
Daichi Hirono6baa16e2015-08-12 13:51:59 +090046 private TestContentResolver mResolver;
Daichi Hironobeac2182015-07-29 16:28:57 +090047 private MtpDocumentsProvider mProvider;
Daichi Hirono6ee4a1c2015-07-31 10:30:05 +090048 private TestMtpManager mMtpManager;
Daichi Hironob999b0c2015-10-27 16:29:18 +090049 private final TestResources mResources = new TestResources();
Daichi Hironodc473442015-11-13 15:42:28 +090050 private MtpDatabase mDatabase;
Daichi Hironobeac2182015-07-29 16:28:57 +090051
52 @Override
Tomasz Mikolajewski4c1d3dd2015-09-02 13:27:46 +090053 public void setUp() throws IOException {
Daichi Hirono6baa16e2015-08-12 13:51:59 +090054 mResolver = new TestContentResolver();
Daichi Hirono6ee4a1c2015-07-31 10:30:05 +090055 mMtpManager = new TestMtpManager(getContext());
Daichi Hironodc473442015-11-13 15:42:28 +090056 }
57
58 @Override
59 public void tearDown() {
Daichi Hironoe1d57712015-11-17 10:55:45 +090060 mProvider.shutdown();
Daichi Hironoe0282dd2015-11-26 15:20:08 +090061 MtpDatabase.deleteDatabase(getContext());
Daichi Hironobeac2182015-07-29 16:28:57 +090062 }
63
Daichi Hironod5152422015-07-15 13:31:51 +090064 public void testOpenAndCloseDevice() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +090065 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono20754c52015-12-15 18:52:26 +090066 mMtpManager.addValidDevice(new MtpDeviceRecord(
67 0,
Daichi Hironof83ccbd2016-02-04 16:58:55 +090068 "Device A",
Daichi Hironoebd24052016-02-06 21:05:57 +090069 null /* deviceKey */,
Daichi Hirono20754c52015-12-15 18:52:26 +090070 false /* unopened */,
71 new MtpRoot[] {
72 new MtpRoot(
73 0 /* deviceId */,
74 1 /* storageId */,
Daichi Hirono20754c52015-12-15 18:52:26 +090075 "Storage A" /* volume description */,
76 1024 /* free space */,
77 2048 /* total space */,
78 "" /* no volume identifier */)
Daichi Hirono1d4779c2016-01-06 16:43:32 +090079 },
Daichi Hirono148954a2016-01-21 19:55:45 +090080 null,
81 null));
Daichi Hironod5152422015-07-15 13:31:51 +090082
Daichi Hironofda74742016-02-01 13:00:31 +090083 mProvider.resumeRootScanner();
Daichi Hirono8b9024f2015-08-12 12:59:09 +090084 mResolver.waitForNotification(ROOTS_URI, 1);
Daichi Hirono09109562015-07-28 20:57:05 +090085
Daichi Hironofda74742016-02-01 13:00:31 +090086 mProvider.openDevice(0);
Daichi Hirono8b9024f2015-08-12 12:59:09 +090087 mResolver.waitForNotification(ROOTS_URI, 2);
Daichi Hironofda74742016-02-01 13:00:31 +090088
89 mProvider.closeDevice(0);
90 mResolver.waitForNotification(ROOTS_URI, 3);
Daichi Hironod5152422015-07-15 13:31:51 +090091 }
92
Daichi Hirono8b9024f2015-08-12 12:59:09 +090093 public void testOpenAndCloseErrorDevice() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +090094 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono8b9024f2015-08-12 12:59:09 +090095 try {
96 mProvider.openDevice(1);
97 fail();
98 } catch (Throwable error) {
99 assertTrue(error instanceof IOException);
100 }
Daichi Hironofda74742016-02-01 13:00:31 +0900101 assertEquals(0, mProvider.getOpenedDeviceIds().length);
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900102
103 // Check if the following notification is the first one or not.
Daichi Hirono20754c52015-12-15 18:52:26 +0900104 mMtpManager.addValidDevice(new MtpDeviceRecord(
105 0,
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900106 "Device A",
Daichi Hironoebd24052016-02-06 21:05:57 +0900107 null /* deviceKey */,
Daichi Hirono20754c52015-12-15 18:52:26 +0900108 false /* unopened */,
109 new MtpRoot[] {
110 new MtpRoot(
111 0 /* deviceId */,
112 1 /* storageId */,
Daichi Hirono20754c52015-12-15 18:52:26 +0900113 "Storage A" /* volume description */,
114 1024 /* free space */,
115 2048 /* total space */,
116 "" /* no volume identifier */)
Daichi Hirono1d4779c2016-01-06 16:43:32 +0900117 },
Daichi Hirono148954a2016-01-21 19:55:45 +0900118 null,
119 null));
Daichi Hironofda74742016-02-01 13:00:31 +0900120 mProvider.resumeRootScanner();
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900121 mResolver.waitForNotification(ROOTS_URI, 1);
Daichi Hironofda74742016-02-01 13:00:31 +0900122 mProvider.openDevice(0);
123 mResolver.waitForNotification(ROOTS_URI, 2);
124 }
125
126 public void testOpenDeviceOnDemand() throws Exception {
127 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
128 mMtpManager.addValidDevice(new MtpDeviceRecord(
129 0,
130 "Device A",
Daichi Hironoebd24052016-02-06 21:05:57 +0900131 null /* deviceKey */,
Daichi Hironofda74742016-02-01 13:00:31 +0900132 false /* unopened */,
133 new MtpRoot[] {
134 new MtpRoot(
135 0 /* deviceId */,
136 1 /* storageId */,
Daichi Hironofda74742016-02-01 13:00:31 +0900137 "Storage A" /* volume description */,
138 1024 /* free space */,
139 2048 /* total space */,
140 "" /* no volume identifier */)
141 },
142 null,
143 null));
144 mMtpManager.setObjectHandles(0, 1, -1, new int[0]);
145 mProvider.resumeRootScanner();
146 mResolver.waitForNotification(ROOTS_URI, 1);
147 final String[] columns = new String[] {
148 DocumentsContract.Root.COLUMN_TITLE,
149 DocumentsContract.Root.COLUMN_DOCUMENT_ID
150 };
151 try (final Cursor cursor = mProvider.queryRoots(columns)) {
152 assertEquals(1, cursor.getCount());
153 assertTrue(cursor.moveToNext());
154 assertEquals("Device A", cursor.getString(0));
155 assertEquals(1, cursor.getLong(1));
156 }
157 {
158 final int [] openedDevice = mProvider.getOpenedDeviceIds();
159 assertEquals(0, openedDevice.length);
160 }
161 // Device is opened automatically when querying its children.
162 try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {}
163
164 {
165 final int [] openedDevice = mProvider.getOpenedDeviceIds();
166 assertEquals(1, openedDevice.length);
167 assertEquals(0, openedDevice[0]);
168 }
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900169 }
170
Daichi Hirono09109562015-07-28 20:57:05 +0900171 public void testQueryRoots() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900172 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono20754c52015-12-15 18:52:26 +0900173 mMtpManager.addValidDevice(new MtpDeviceRecord(
174 0,
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900175 "Device A",
Daichi Hironoebd24052016-02-06 21:05:57 +0900176 "Device key A",
Daichi Hirono20754c52015-12-15 18:52:26 +0900177 false /* unopened */,
178 new MtpRoot[] {
179 new MtpRoot(
180 0 /* deviceId */,
181 1 /* storageId */,
Daichi Hirono20754c52015-12-15 18:52:26 +0900182 "Storage A" /* volume description */,
183 1024 /* free space */,
184 2048 /* total space */,
185 "" /* no volume identifier */)
Daichi Hirono1d4779c2016-01-06 16:43:32 +0900186 },
Daichi Hirono148954a2016-01-21 19:55:45 +0900187 null,
188 null));
Daichi Hirono20754c52015-12-15 18:52:26 +0900189 mMtpManager.addValidDevice(new MtpDeviceRecord(
190 1,
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900191 "Device B",
Daichi Hironoebd24052016-02-06 21:05:57 +0900192 "Device key B",
Daichi Hirono20754c52015-12-15 18:52:26 +0900193 false /* unopened */,
194 new MtpRoot[] {
195 new MtpRoot(
196 1 /* deviceId */,
197 1 /* storageId */,
Daichi Hirono20754c52015-12-15 18:52:26 +0900198 "Storage B" /* volume description */,
199 2048 /* free space */,
200 4096 /* total space */,
201 "Identifier B" /* no volume identifier */)
Daichi Hirono1d4779c2016-01-06 16:43:32 +0900202 },
Daichi Hirono148954a2016-01-21 19:55:45 +0900203 null,
204 null));
Daichi Hironod5152422015-07-15 13:31:51 +0900205
Daichi Hirono09109562015-07-28 20:57:05 +0900206 {
Daichi Hironobeac2182015-07-29 16:28:57 +0900207 mProvider.openDevice(0);
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900208 mResolver.waitForNotification(ROOTS_URI, 1);
Daichi Hironobeac2182015-07-29 16:28:57 +0900209 final Cursor cursor = mProvider.queryRoots(null);
Daichi Hirono81d48532015-12-16 15:03:19 +0900210 assertEquals(2, cursor.getCount());
Daichi Hirono09109562015-07-28 20:57:05 +0900211 cursor.moveToNext();
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900212 assertEquals("1", cursor.getString(0));
Tomasz Mikolajewskic0834cd2015-08-26 12:31:45 +0900213 assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
Daichi Hirono39795da2015-12-02 10:56:44 +0900214 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
Daichi Hirono17c8d8b2015-10-12 11:28:46 -0700215 assertEquals("Device A Storage A", cursor.getString(3));
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900216 assertEquals("1", cursor.getString(4));
Daichi Hirono09109562015-07-28 20:57:05 +0900217 assertEquals(1024, cursor.getInt(5));
Daichi Hironod5152422015-07-15 13:31:51 +0900218 }
219
Daichi Hirono09109562015-07-28 20:57:05 +0900220 {
Daichi Hironobeac2182015-07-29 16:28:57 +0900221 mProvider.openDevice(1);
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900222 mResolver.waitForNotification(ROOTS_URI, 2);
Daichi Hironobeac2182015-07-29 16:28:57 +0900223 final Cursor cursor = mProvider.queryRoots(null);
Daichi Hirono09109562015-07-28 20:57:05 +0900224 assertEquals(2, cursor.getCount());
225 cursor.moveToNext();
226 cursor.moveToNext();
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900227 assertEquals("2", cursor.getString(0));
Tomasz Mikolajewskic0834cd2015-08-26 12:31:45 +0900228 assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
Daichi Hirono39795da2015-12-02 10:56:44 +0900229 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
Daichi Hirono17c8d8b2015-10-12 11:28:46 -0700230 assertEquals("Device B Storage B", cursor.getString(3));
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900231 assertEquals("2", cursor.getString(4));
Daichi Hirono09109562015-07-28 20:57:05 +0900232 assertEquals(2048, cursor.getInt(5));
Daichi Hironod5152422015-07-15 13:31:51 +0900233 }
Daichi Hirono09109562015-07-28 20:57:05 +0900234 }
Daichi Hironod5152422015-07-15 13:31:51 +0900235
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900236 public void testQueryRoots_error() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900237 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono1d4779c2016-01-06 16:43:32 +0900238 mMtpManager.addValidDevice(new MtpDeviceRecord(
Daichi Hironoebd24052016-02-06 21:05:57 +0900239 0,
240 "Device A",
241 "Device key A",
242 false /* unopened */,
243 new MtpRoot[0],
244 null,
245 null));
Daichi Hirono20754c52015-12-15 18:52:26 +0900246 mMtpManager.addValidDevice(new MtpDeviceRecord(
247 1,
Daichi Hironofda74742016-02-01 13:00:31 +0900248 "Device B",
Daichi Hironoebd24052016-02-06 21:05:57 +0900249 "Device key B",
Daichi Hirono20754c52015-12-15 18:52:26 +0900250 false /* unopened */,
251 new MtpRoot[] {
252 new MtpRoot(
253 1 /* deviceId */,
254 1 /* storageId */,
Daichi Hirono20754c52015-12-15 18:52:26 +0900255 "Storage B" /* volume description */,
256 2048 /* free space */,
257 4096 /* total space */,
258 "Identifier B" /* no volume identifier */)
Daichi Hirono1d4779c2016-01-06 16:43:32 +0900259 },
Daichi Hirono148954a2016-01-21 19:55:45 +0900260 null,
261 null));
Daichi Hirono09109562015-07-28 20:57:05 +0900262 {
Daichi Hironobeac2182015-07-29 16:28:57 +0900263 mProvider.openDevice(0);
Daichi Hironofda74742016-02-01 13:00:31 +0900264 mProvider.resumeRootScanner();
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900265 mResolver.waitForNotification(ROOTS_URI, 1);
266
Daichi Hironofda74742016-02-01 13:00:31 +0900267 mProvider.openDevice(1);
268 mProvider.resumeRootScanner();
269 mResolver.waitForNotification(ROOTS_URI, 2);
270
Daichi Hironobeac2182015-07-29 16:28:57 +0900271 final Cursor cursor = mProvider.queryRoots(null);
Daichi Hirono81d48532015-12-16 15:03:19 +0900272 assertEquals(2, cursor.getCount());
273
274 cursor.moveToNext();
275 assertEquals("1", cursor.getString(0));
276 assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
277 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
278 assertEquals("Device A", cursor.getString(3));
279 assertEquals("1", cursor.getString(4));
280 assertEquals(0, cursor.getInt(5));
281
Daichi Hirono09109562015-07-28 20:57:05 +0900282 cursor.moveToNext();
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900283 assertEquals("2", cursor.getString(0));
Tomasz Mikolajewskic0834cd2015-08-26 12:31:45 +0900284 assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1));
Daichi Hirono39795da2015-12-02 10:56:44 +0900285 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
Daichi Hirono17c8d8b2015-10-12 11:28:46 -0700286 assertEquals("Device B Storage B", cursor.getString(3));
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900287 assertEquals("2", cursor.getString(4));
Daichi Hirono09109562015-07-28 20:57:05 +0900288 assertEquals(2048, cursor.getInt(5));
Daichi Hironod5152422015-07-15 13:31:51 +0900289 }
290 }
291
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900292 public void testQueryDocument() throws IOException, InterruptedException, TimeoutException {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900293 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900294 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900295 setupDocuments(
296 0,
297 0,
298 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
299 "1",
300 new MtpObjectInfo[] {
301 new MtpObjectInfo.Builder()
302 .setObjectHandle(100)
303 .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
304 .setName("image.jpg")
305 .setDateModified(1422716400000L)
306 .setCompressedSize(1024 * 1024 * 5)
307 .setThumbCompressedSize(50 * 1024)
308 .build()
309 });
310
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000311 final Cursor cursor = mProvider.queryDocument("3", null);
Daichi Hironoe5323b72015-07-29 16:10:47 +0900312 assertEquals(1, cursor.getCount());
313
314 cursor.moveToNext();
Daichi Hirono47eb1922015-11-16 13:01:31 +0900315
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000316 assertEquals("3", cursor.getString(0));
Daichi Hironoe5323b72015-07-29 16:10:47 +0900317 assertEquals("image/jpeg", cursor.getString(1));
318 assertEquals("image.jpg", cursor.getString(2));
319 assertEquals(1422716400000L, cursor.getLong(3));
320 assertEquals(
321 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900322 DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
Daichi Hironoe5323b72015-07-29 16:10:47 +0900323 DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL,
324 cursor.getInt(4));
325 assertEquals(1024 * 1024 * 5, cursor.getInt(5));
326 }
327
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900328 public void testQueryDocument_directory()
329 throws IOException, InterruptedException, TimeoutException {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900330 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900331 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900332 setupDocuments(
333 0,
334 0,
335 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
336 "1",
337 new MtpObjectInfo[] {
338 new MtpObjectInfo.Builder()
339 .setObjectHandle(2)
340 .setStorageId(1)
341 .setFormat(MtpConstants.FORMAT_ASSOCIATION)
342 .setName("directory")
343 .setDateModified(1422716400000L)
344 .build()
345 });
346
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000347 final Cursor cursor = mProvider.queryDocument("3", null);
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900348 assertEquals(1, cursor.getCount());
349
350 cursor.moveToNext();
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000351 assertEquals("3", cursor.getString(0));
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900352 assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
353 assertEquals("directory", cursor.getString(2));
354 assertEquals(1422716400000L, cursor.getLong(3));
355 assertEquals(
356 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
357 DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
358 DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE,
359 cursor.getInt(4));
360 assertEquals(0, cursor.getInt(5));
361 }
362
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900363 public void testQueryDocument_forRoot()
364 throws IOException, InterruptedException, TimeoutException {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900365 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900366 setupRoots(0, new MtpRoot[] {
Daichi Hironoe5323b72015-07-29 16:10:47 +0900367 new MtpRoot(
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900368 0 /* deviceId */,
Daichi Hironoe5323b72015-07-29 16:10:47 +0900369 1 /* storageId */,
370 "Storage A" /* volume description */,
371 1024 /* free space */,
372 4096 /* total space */,
373 "" /* no volume identifier */)
374 });
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000375 final Cursor cursor = mProvider.queryDocument("2", null);
Daichi Hironoe5323b72015-07-29 16:10:47 +0900376 assertEquals(1, cursor.getCount());
377
378 cursor.moveToNext();
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000379 assertEquals("2", cursor.getString(0));
Daichi Hironoe5323b72015-07-29 16:10:47 +0900380 assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900381 assertEquals("Storage A", cursor.getString(2));
Daichi Hironoe5323b72015-07-29 16:10:47 +0900382 assertTrue(cursor.isNull(3));
383 assertEquals(0, cursor.getInt(4));
384 assertEquals(3072, cursor.getInt(5));
385 }
386
Daichi Hirono124d0602015-08-11 17:08:35 +0900387 public void testQueryChildDocuments() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900388 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900389 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900390 setupDocuments(
391 0,
392 0,
393 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
394 "1",
395 new MtpObjectInfo[] {
396 new MtpObjectInfo.Builder()
397 .setObjectHandle(100)
398 .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
399 .setName("image.jpg")
400 .setCompressedSize(1024 * 1024 * 5)
401 .setThumbCompressedSize(5 * 1024)
402 .setProtectionStatus(MtpConstants.PROTECTION_STATUS_READ_ONLY)
403 .build()
404 });
Daichi Hirono47eb1922015-11-16 13:01:31 +0900405
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900406 final Cursor cursor = mProvider.queryChildDocuments("1", null, null);
Daichi Hirono124d0602015-08-11 17:08:35 +0900407 assertEquals(1, cursor.getCount());
408
409 assertTrue(cursor.moveToNext());
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000410 assertEquals("3", cursor.getString(0));
Daichi Hirono124d0602015-08-11 17:08:35 +0900411 assertEquals("image/jpeg", cursor.getString(1));
412 assertEquals("image.jpg", cursor.getString(2));
413 assertEquals(0, cursor.getLong(3));
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900414 assertEquals(DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL, cursor.getInt(4));
Daichi Hirono124d0602015-08-11 17:08:35 +0900415 assertEquals(1024 * 1024 * 5, cursor.getInt(5));
416
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900417 cursor.close();
Daichi Hirono124d0602015-08-11 17:08:35 +0900418 }
419
420 public void testQueryChildDocuments_cursorError() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900421 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono124d0602015-08-11 17:08:35 +0900422 try {
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900423 mProvider.queryChildDocuments("1", null, null);
Daichi Hirono124d0602015-08-11 17:08:35 +0900424 fail();
425 } catch (Throwable error) {
426 assertTrue(error instanceof FileNotFoundException);
427 }
428 }
429
430 public void testQueryChildDocuments_documentError() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900431 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900432 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
Daichi Hirono124d0602015-08-11 17:08:35 +0900433 mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
434 try {
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900435 mProvider.queryChildDocuments("1", null, null);
Daichi Hirono124d0602015-08-11 17:08:35 +0900436 fail();
437 } catch (Throwable error) {
438 assertTrue(error instanceof FileNotFoundException);
439 }
440 }
441
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900442 public void testDeleteDocument() throws IOException, InterruptedException, TimeoutException {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900443 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900444 setupRoots(0, new MtpRoot[] {
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900445 new MtpRoot(0, 0, "Storage", 0, 0, "")
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900446 });
447 setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
448 new MtpObjectInfo.Builder()
449 .setName("test.txt")
450 .setObjectHandle(1)
451 .setParent(-1)
452 .build()
453 });
454
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000455 mProvider.deleteDocument("3");
Daichi Hirono5fecc6c2015-08-04 17:45:51 +0900456 assertEquals(1, mResolver.getChangeCount(
457 DocumentsContract.buildChildDocumentsUri(
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900458 MtpDocumentsProvider.AUTHORITY, "1")));
Daichi Hirono5fecc6c2015-08-04 17:45:51 +0900459 }
460
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900461 public void testDeleteDocument_error()
462 throws IOException, InterruptedException, TimeoutException {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900463 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900464 setupRoots(0, new MtpRoot[] {
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900465 new MtpRoot(0, 0, "Storage", 0, 0, "")
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900466 });
467 setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
468 new MtpObjectInfo.Builder()
469 .setName("test.txt")
470 .setObjectHandle(1)
471 .setParent(-1)
472 .build()
473 });
Daichi Hirono5fecc6c2015-08-04 17:45:51 +0900474 try {
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000475 mProvider.deleteDocument("4");
Daichi Hirono5fecc6c2015-08-04 17:45:51 +0900476 fail();
477 } catch (Throwable e) {
478 assertTrue(e instanceof IOException);
479 }
480 assertEquals(0, mResolver.getChangeCount(
481 DocumentsContract.buildChildDocumentsUri(
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900482 MtpDocumentsProvider.AUTHORITY, "1")));
483 }
484
Daichi Hironob36b1552016-01-25 14:26:14 +0900485 public void testOpenDocument() throws Exception {
486 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
487 setupRoots(0, new MtpRoot[] {
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900488 new MtpRoot(0, 0, "Storage", 0, 0, "")
Daichi Hironob36b1552016-01-25 14:26:14 +0900489 });
490 final byte[] bytes = "Hello world".getBytes();
491 setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
492 new MtpObjectInfo.Builder()
493 .setName("test.txt")
494 .setObjectHandle(1)
495 .setCompressedSize(bytes.length)
496 .setParent(-1)
497 .build()
498 });
499 mMtpManager.setImportFileBytes(0, 1, bytes);
500 try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) {
501 final byte[] readBytes = new byte[5];
502 assertEquals(6, Os.lseek(fd.getFileDescriptor(), 6, OsConstants.SEEK_SET));
503 assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5));
504 assertTrue(Arrays.equals("world".getBytes(), readBytes));
505
506 assertEquals(0, Os.lseek(fd.getFileDescriptor(), 0, OsConstants.SEEK_SET));
507 assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5));
508 assertTrue(Arrays.equals("Hello".getBytes(), readBytes));
509 }
510 }
511
512 public void testOpenDocument_shortBytes() throws Exception {
513 mMtpManager = new TestMtpManager(getContext()) {
514 @Override
515 MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
516 if (objectHandle == 1) {
517 return new MtpObjectInfo.Builder(super.getObjectInfo(deviceId, objectHandle))
518 .setObjectHandle(1).setCompressedSize(1024 * 1024).build();
519 }
520
521 return super.getObjectInfo(deviceId, objectHandle);
522 }
523 };
524 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
525 setupRoots(0, new MtpRoot[] {
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900526 new MtpRoot(0, 0, "Storage", 0, 0, "")
Daichi Hironob36b1552016-01-25 14:26:14 +0900527 });
528 final byte[] bytes = "Hello world".getBytes();
529 setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
530 new MtpObjectInfo.Builder()
531 .setName("test.txt")
532 .setObjectHandle(1)
533 .setCompressedSize(bytes.length)
534 .setParent(-1)
535 .build()
536 });
537 mMtpManager.setImportFileBytes(0, 1, bytes);
538 try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) {
539 final byte[] readBytes = new byte[1024 * 1024];
540 assertEquals(11, Os.read(fd.getFileDescriptor(), readBytes, 0, readBytes.length));
541 }
542 }
543
Daichi Hironoc18f8072016-02-10 14:59:52 -0800544 public void testBusyDevice() throws Exception {
545 mMtpManager = new TestMtpManager(getContext()) {
546 @Override
547 void openDevice(int deviceId) throws IOException {
548 throw new BusyDeviceException();
549 }
550 };
551 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
552 mMtpManager.addValidDevice(new MtpDeviceRecord(
Daichi Hironoebd24052016-02-06 21:05:57 +0900553 0, "Device A", null /* deviceKey */, false /* unopened */, new MtpRoot[0], null,
554 null));
Daichi Hironoc18f8072016-02-10 14:59:52 -0800555
556 mProvider.resumeRootScanner();
557 mResolver.waitForNotification(ROOTS_URI, 1);
558
559 try (final Cursor cursor = mProvider.queryRoots(null)) {
560 assertEquals(1, cursor.getCount());
561 }
562
563 try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
564 assertEquals(0, cursor.getCount());
565 assertEquals(
566 "error_busy_device",
567 cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
568 }
569 }
570
Daichi Hirono29657762016-02-10 16:55:37 -0800571 public void testLockedDevice() throws Exception {
572 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
573 mMtpManager.addValidDevice(new MtpDeviceRecord(
Daichi Hironoebd24052016-02-06 21:05:57 +0900574 0, "Device A", null, false /* unopened */, new MtpRoot[0], null, null));
Daichi Hirono29657762016-02-10 16:55:37 -0800575
576 mProvider.resumeRootScanner();
577 mResolver.waitForNotification(ROOTS_URI, 1);
578
579 try (final Cursor cursor = mProvider.queryRoots(null)) {
580 assertEquals(1, cursor.getCount());
581 }
582
583 try (final Cursor cursor = mProvider.queryChildDocuments("1", null, null)) {
584 assertEquals(0, cursor.getCount());
585 assertEquals(
586 "error_locked_device",
587 cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
588 }
589 }
590
Daichi Hirono4e94b8d2016-02-21 22:42:41 +0900591 public void testMappingDisconnectedDocuments() throws Exception {
592 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
593 mMtpManager.addValidDevice(new MtpDeviceRecord(
594 0,
595 "Device A",
596 "device key",
597 true /* unopened */,
598 new MtpRoot[] {
599 new MtpRoot(
600 0 /* deviceId */,
601 1 /* storageId */,
602 "Storage A" /* volume description */,
603 1024 /* free space */,
604 2048 /* total space */,
605 "" /* no volume identifier */)
606 },
607 null,
608 null));
609
610 final String[] names = strings("Directory A", "Directory B", "Directory C");
611 final int objectHandleOffset = 100;
612 for (int i = 0; i < names.length; i++) {
613 final int parentHandle = i == 0 ?
614 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN : objectHandleOffset + i - 1;
615 final int objectHandle = i + objectHandleOffset;
616 mMtpManager.setObjectHandles(0, 1, parentHandle, new int[] { objectHandle });
617 mMtpManager.setObjectInfo(
618 0,
619 new MtpObjectInfo.Builder()
620 .setName(names[i])
621 .setObjectHandle(objectHandle)
622 .setFormat(MtpConstants.FORMAT_ASSOCIATION)
623 .setStorageId(1)
624 .build());
625 }
626
627 mProvider.resumeRootScanner();
628 mResolver.waitForNotification(ROOTS_URI, 1);
629
630 final int documentIdOffset = 2;
631 for (int i = 0; i < names.length; i++) {
632 try (final Cursor cursor = mProvider.queryChildDocuments(
633 String.valueOf(documentIdOffset + i),
634 strings(Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME),
635 null)) {
636 assertEquals(1, cursor.getCount());
637 cursor.moveToNext();
638 assertEquals(String.valueOf(documentIdOffset + i + 1), cursor.getString(0));
639 assertEquals(names[i], cursor.getString(1));
640 }
641 }
642
643 mProvider.closeDevice(0);
644 mResolver.waitForNotification(ROOTS_URI, 2);
645
646 mProvider.openDevice(0);
647 mResolver.waitForNotification(ROOTS_URI, 3);
648
649 for (int i = 0; i < names.length; i++) {
650 mResolver.waitForNotification(DocumentsContract.buildChildDocumentsUri(
651 MtpDocumentsProvider.AUTHORITY,
652 String.valueOf(documentIdOffset + i)), 1);
653 try (final Cursor cursor = mProvider.queryChildDocuments(
654 String.valueOf(documentIdOffset + i),
655 strings(Document.COLUMN_DOCUMENT_ID),
656 null)) {
657 assertEquals(1, cursor.getCount());
658 cursor.moveToNext();
659 assertEquals(String.valueOf(documentIdOffset + i + 1), cursor.getString(0));
660 }
661 }
662 }
663
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900664 private void setupProvider(int flag) {
665 mDatabase = new MtpDatabase(getContext(), flag);
666 mProvider = new MtpDocumentsProvider();
Daichi Hironob36b1552016-01-25 14:26:14 +0900667 final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
668 assertTrue(mProvider.onCreateForTesting(
Daichi Hironofda74742016-02-01 13:00:31 +0900669 mResources,
670 mMtpManager,
671 mResolver,
672 mDatabase,
673 storageManager,
674 new TestServiceIntentSender()));
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900675 }
676
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900677 private String[] getStrings(Cursor cursor) {
678 try {
679 final String[] results = new String[cursor.getCount()];
680 for (int i = 0; cursor.moveToNext(); i++) {
681 results[i] = cursor.getString(0);
682 }
683 return results;
684 } finally {
685 cursor.close();
686 }
687 }
688
689 private String[] setupRoots(int deviceId, MtpRoot[] roots)
Daichi Hirono20754c52015-12-15 18:52:26 +0900690 throws InterruptedException, TimeoutException, IOException {
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900691 final int changeCount = mResolver.getChangeCount(ROOTS_URI);
Daichi Hirono20754c52015-12-15 18:52:26 +0900692 mMtpManager.addValidDevice(
Daichi Hironoebd24052016-02-06 21:05:57 +0900693 new MtpDeviceRecord(deviceId, "Device", null /* deviceKey */, false /* unopened */,
694 roots, null, null));
Daichi Hirono20754c52015-12-15 18:52:26 +0900695 mProvider.openDevice(deviceId);
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900696 mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
697 return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
698 }
699
700 private String[] setupDocuments(
701 int deviceId,
702 int storageId,
703 int parentHandle,
704 String parentDocumentId,
705 MtpObjectInfo[] objects) throws FileNotFoundException {
706 final int[] handles = new int[objects.length];
707 int i = 0;
708 for (final MtpObjectInfo info : objects) {
709 handles[i] = info.getObjectHandle();
710 mMtpManager.setObjectInfo(deviceId, info);
711 }
712 mMtpManager.setObjectHandles(deviceId, storageId, parentHandle, handles);
713 return getStrings(mProvider.queryChildDocuments(
714 parentDocumentId, strings(DocumentsContract.Document.COLUMN_DOCUMENT_ID), null));
Daichi Hirono5fecc6c2015-08-04 17:45:51 +0900715 }
Daichi Hirono9a05d6b2015-07-09 15:36:42 +0900716}