blob: 65c86dfb6d4c3f5294a45dc18d9e5b5ebd51deef [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 Hironob9ffa2a2016-11-01 18:41:43 +090026import android.provider.DocumentsContract.Path;
Daichi Hirono09109562015-07-28 20:57:05 +090027import android.provider.DocumentsContract.Root;
Daichi Hironob36b1552016-01-25 14:26:14 +090028import android.system.Os;
29import android.system.OsConstants;
Tomasz Mikolajewskibb430fa2015-08-25 18:34:30 +090030import android.provider.DocumentsContract;
Daichi Hirono9a05d6b2015-07-09 15:36:42 +090031import android.test.AndroidTestCase;
Daichi Hironob36b1552016-01-25 14:26:14 +090032import android.test.suitebuilder.annotation.MediumTest;
Daichi Hirono9a05d6b2015-07-09 15:36:42 +090033
Daichi Hirono5fecc6c2015-08-04 17:45:51 +090034import java.io.FileNotFoundException;
Daichi Hironod5152422015-07-15 13:31:51 +090035import java.io.IOException;
Daichi Hironob36b1552016-01-25 14:26:14 +090036import java.util.Arrays;
Daichi Hironob9ffa2a2016-11-01 18:41:43 +090037import java.util.LinkedList;
Daichi Hironob9ffa2a2016-11-01 18:41:43 +090038import java.util.Queue;
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +090039import java.util.concurrent.TimeoutException;
40
Daichi Hirono259ce802015-11-20 17:51:53 +090041import static com.android.mtp.MtpDatabase.strings;
Daichi Hirono0f325372016-02-21 15:50:30 +090042import static com.android.mtp.TestUtil.OPERATIONS_SUPPORTED;
Daichi Hironod5152422015-07-15 13:31:51 +090043
Daichi Hironob36b1552016-01-25 14:26:14 +090044@MediumTest
Daichi Hirono9a05d6b2015-07-09 15:36:42 +090045public class MtpDocumentsProviderTest extends AndroidTestCase {
Daichi Hirono8b9024f2015-08-12 12:59:09 +090046 private final static Uri ROOTS_URI =
47 DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY);
Daichi Hirono6baa16e2015-08-12 13:51:59 +090048 private TestContentResolver mResolver;
Daichi Hironobeac2182015-07-29 16:28:57 +090049 private MtpDocumentsProvider mProvider;
Daichi Hirono6ee4a1c2015-07-31 10:30:05 +090050 private TestMtpManager mMtpManager;
Daichi Hironob999b0c2015-10-27 16:29:18 +090051 private final TestResources mResources = new TestResources();
Daichi Hironodc473442015-11-13 15:42:28 +090052 private MtpDatabase mDatabase;
Daichi Hironobeac2182015-07-29 16:28:57 +090053
54 @Override
Tomasz Mikolajewski4c1d3dd2015-09-02 13:27:46 +090055 public void setUp() throws IOException {
Daichi Hirono6baa16e2015-08-12 13:51:59 +090056 mResolver = new TestContentResolver();
Daichi Hirono6ee4a1c2015-07-31 10:30:05 +090057 mMtpManager = new TestMtpManager(getContext());
Daichi Hironodc473442015-11-13 15:42:28 +090058 }
59
60 @Override
61 public void tearDown() {
Daichi Hironoe1d57712015-11-17 10:55:45 +090062 mProvider.shutdown();
Daichi Hironoe0282dd2015-11-26 15:20:08 +090063 MtpDatabase.deleteDatabase(getContext());
Daichi Hironobeac2182015-07-29 16:28:57 +090064 }
65
Daichi Hironod5152422015-07-15 13:31:51 +090066 public void testOpenAndCloseDevice() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +090067 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono20754c52015-12-15 18:52:26 +090068 mMtpManager.addValidDevice(new MtpDeviceRecord(
69 0,
Daichi Hironof83ccbd2016-02-04 16:58:55 +090070 "Device A",
Daichi Hironoebd24052016-02-06 21:05:57 +090071 null /* deviceKey */,
Daichi Hirono20754c52015-12-15 18:52:26 +090072 false /* unopened */,
73 new MtpRoot[] {
74 new MtpRoot(
75 0 /* deviceId */,
76 1 /* storageId */,
Daichi Hirono20754c52015-12-15 18:52:26 +090077 "Storage A" /* volume description */,
78 1024 /* free space */,
79 2048 /* total space */,
80 "" /* no volume identifier */)
Daichi Hirono1d4779c2016-01-06 16:43:32 +090081 },
Daichi Hirono0f325372016-02-21 15:50:30 +090082 OPERATIONS_SUPPORTED,
Daichi Hirono148954a2016-01-21 19:55:45 +090083 null));
Daichi Hironod5152422015-07-15 13:31:51 +090084
Daichi Hironofda74742016-02-01 13:00:31 +090085 mProvider.resumeRootScanner();
Daichi Hirono8b9024f2015-08-12 12:59:09 +090086 mResolver.waitForNotification(ROOTS_URI, 1);
Daichi Hirono09109562015-07-28 20:57:05 +090087
Daichi Hironofda74742016-02-01 13:00:31 +090088 mProvider.openDevice(0);
Daichi Hirono8b9024f2015-08-12 12:59:09 +090089 mResolver.waitForNotification(ROOTS_URI, 2);
Daichi Hironofda74742016-02-01 13:00:31 +090090
91 mProvider.closeDevice(0);
92 mResolver.waitForNotification(ROOTS_URI, 3);
Daichi Hironod5152422015-07-15 13:31:51 +090093 }
94
Daichi Hirono8b9024f2015-08-12 12:59:09 +090095 public void testOpenAndCloseErrorDevice() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +090096 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono8b9024f2015-08-12 12:59:09 +090097 try {
98 mProvider.openDevice(1);
99 fail();
100 } catch (Throwable error) {
101 assertTrue(error instanceof IOException);
102 }
Daichi Hirono0f325372016-02-21 15:50:30 +0900103 assertEquals(0, mProvider.getOpenedDeviceRecordsCache().length);
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900104
105 // Check if the following notification is the first one or not.
Daichi Hirono20754c52015-12-15 18:52:26 +0900106 mMtpManager.addValidDevice(new MtpDeviceRecord(
107 0,
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900108 "Device A",
Daichi Hironoebd24052016-02-06 21:05:57 +0900109 null /* deviceKey */,
Daichi Hirono20754c52015-12-15 18:52:26 +0900110 false /* unopened */,
111 new MtpRoot[] {
112 new MtpRoot(
113 0 /* deviceId */,
114 1 /* storageId */,
Daichi Hirono20754c52015-12-15 18:52:26 +0900115 "Storage A" /* volume description */,
116 1024 /* free space */,
117 2048 /* total space */,
118 "" /* no volume identifier */)
Daichi Hirono1d4779c2016-01-06 16:43:32 +0900119 },
Daichi Hirono0f325372016-02-21 15:50:30 +0900120 OPERATIONS_SUPPORTED,
Daichi Hirono148954a2016-01-21 19:55:45 +0900121 null));
Daichi Hironofda74742016-02-01 13:00:31 +0900122 mProvider.resumeRootScanner();
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900123 mResolver.waitForNotification(ROOTS_URI, 1);
Daichi Hironofda74742016-02-01 13:00:31 +0900124 mProvider.openDevice(0);
125 mResolver.waitForNotification(ROOTS_URI, 2);
126 }
127
128 public void testOpenDeviceOnDemand() throws Exception {
129 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
130 mMtpManager.addValidDevice(new MtpDeviceRecord(
131 0,
132 "Device A",
Daichi Hironoebd24052016-02-06 21:05:57 +0900133 null /* deviceKey */,
Daichi Hironofda74742016-02-01 13:00:31 +0900134 false /* unopened */,
135 new MtpRoot[] {
136 new MtpRoot(
137 0 /* deviceId */,
138 1 /* storageId */,
Daichi Hironofda74742016-02-01 13:00:31 +0900139 "Storage A" /* volume description */,
140 1024 /* free space */,
141 2048 /* total space */,
142 "" /* no volume identifier */)
143 },
Daichi Hirono0f325372016-02-21 15:50:30 +0900144 OPERATIONS_SUPPORTED,
Daichi Hironofda74742016-02-01 13:00:31 +0900145 null));
146 mMtpManager.setObjectHandles(0, 1, -1, new int[0]);
147 mProvider.resumeRootScanner();
148 mResolver.waitForNotification(ROOTS_URI, 1);
149 final String[] columns = new String[] {
150 DocumentsContract.Root.COLUMN_TITLE,
151 DocumentsContract.Root.COLUMN_DOCUMENT_ID
152 };
153 try (final Cursor cursor = mProvider.queryRoots(columns)) {
154 assertEquals(1, cursor.getCount());
155 assertTrue(cursor.moveToNext());
156 assertEquals("Device A", cursor.getString(0));
157 assertEquals(1, cursor.getLong(1));
158 }
159 {
Daichi Hirono0f325372016-02-21 15:50:30 +0900160 final MtpDeviceRecord[] openedDevice = mProvider.getOpenedDeviceRecordsCache();
Daichi Hironofda74742016-02-01 13:00:31 +0900161 assertEquals(0, openedDevice.length);
162 }
163 // Device is opened automatically when querying its children.
Steve McKay29c3f682016-12-16 14:52:59 -0800164 try (final Cursor cursor = mProvider.queryChildDocuments("1", null, (String) null)) {}
Daichi Hironofda74742016-02-01 13:00:31 +0900165
166 {
Daichi Hirono0f325372016-02-21 15:50:30 +0900167 final MtpDeviceRecord[] openedDevice = mProvider.getOpenedDeviceRecordsCache();
Daichi Hironofda74742016-02-01 13:00:31 +0900168 assertEquals(1, openedDevice.length);
Daichi Hirono0f325372016-02-21 15:50:30 +0900169 assertEquals(0, openedDevice[0].deviceId);
Daichi Hironofda74742016-02-01 13:00:31 +0900170 }
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900171 }
172
Daichi Hirono09109562015-07-28 20:57:05 +0900173 public void testQueryRoots() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900174 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono20754c52015-12-15 18:52:26 +0900175 mMtpManager.addValidDevice(new MtpDeviceRecord(
176 0,
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900177 "Device A",
Daichi Hironoebd24052016-02-06 21:05:57 +0900178 "Device key A",
Daichi Hirono20754c52015-12-15 18:52:26 +0900179 false /* unopened */,
180 new MtpRoot[] {
181 new MtpRoot(
182 0 /* deviceId */,
183 1 /* storageId */,
Daichi Hirono20754c52015-12-15 18:52:26 +0900184 "Storage A" /* volume description */,
185 1024 /* free space */,
186 2048 /* total space */,
187 "" /* no volume identifier */)
Daichi Hirono1d4779c2016-01-06 16:43:32 +0900188 },
Daichi Hirono0f325372016-02-21 15:50:30 +0900189 OPERATIONS_SUPPORTED,
Daichi Hirono148954a2016-01-21 19:55:45 +0900190 null));
Daichi Hirono20754c52015-12-15 18:52:26 +0900191 mMtpManager.addValidDevice(new MtpDeviceRecord(
192 1,
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900193 "Device B",
Daichi Hironoebd24052016-02-06 21:05:57 +0900194 "Device key B",
Daichi Hirono20754c52015-12-15 18:52:26 +0900195 false /* unopened */,
196 new MtpRoot[] {
197 new MtpRoot(
198 1 /* deviceId */,
199 1 /* storageId */,
Daichi Hirono20754c52015-12-15 18:52:26 +0900200 "Storage B" /* volume description */,
201 2048 /* free space */,
202 4096 /* total space */,
203 "Identifier B" /* no volume identifier */)
Daichi Hirono1d4779c2016-01-06 16:43:32 +0900204 },
Daichi Hirono0f325372016-02-21 15:50:30 +0900205 new int[0] /* No operations supported */,
Daichi Hirono148954a2016-01-21 19:55:45 +0900206 null));
Daichi Hironod5152422015-07-15 13:31:51 +0900207
Daichi Hirono09109562015-07-28 20:57:05 +0900208 {
Daichi Hironobeac2182015-07-29 16:28:57 +0900209 mProvider.openDevice(0);
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900210 mResolver.waitForNotification(ROOTS_URI, 1);
Daichi Hironobeac2182015-07-29 16:28:57 +0900211 final Cursor cursor = mProvider.queryRoots(null);
Daichi Hirono81d48532015-12-16 15:03:19 +0900212 assertEquals(2, cursor.getCount());
Daichi Hirono09109562015-07-28 20:57:05 +0900213 cursor.moveToNext();
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900214 assertEquals("1", cursor.getString(0));
Daichi Hirono83c679e72016-08-18 15:13:36 +0900215 assertEquals(
216 Root.FLAG_SUPPORTS_IS_CHILD |
217 Root.FLAG_SUPPORTS_CREATE |
218 Root.FLAG_LOCAL_ONLY,
219 cursor.getInt(1));
Daichi Hirono39795da2015-12-02 10:56:44 +0900220 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
Daichi Hirono17c8d8b2015-10-12 11:28:46 -0700221 assertEquals("Device A Storage A", cursor.getString(3));
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900222 assertEquals("1", cursor.getString(4));
Daichi Hirono09109562015-07-28 20:57:05 +0900223 assertEquals(1024, cursor.getInt(5));
Daichi Hironod5152422015-07-15 13:31:51 +0900224 }
225
Daichi Hirono09109562015-07-28 20:57:05 +0900226 {
Daichi Hironobeac2182015-07-29 16:28:57 +0900227 mProvider.openDevice(1);
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900228 mResolver.waitForNotification(ROOTS_URI, 2);
Daichi Hironobeac2182015-07-29 16:28:57 +0900229 final Cursor cursor = mProvider.queryRoots(null);
Daichi Hirono09109562015-07-28 20:57:05 +0900230 assertEquals(2, cursor.getCount());
231 cursor.moveToNext();
232 cursor.moveToNext();
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900233 assertEquals("2", cursor.getString(0));
Daichi Hirono83c679e72016-08-18 15:13:36 +0900234 assertEquals(
235 Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_LOCAL_ONLY, cursor.getInt(1));
Daichi Hirono39795da2015-12-02 10:56:44 +0900236 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
Daichi Hirono17c8d8b2015-10-12 11:28:46 -0700237 assertEquals("Device B Storage B", cursor.getString(3));
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900238 assertEquals("2", cursor.getString(4));
Daichi Hirono09109562015-07-28 20:57:05 +0900239 assertEquals(2048, cursor.getInt(5));
Daichi Hironod5152422015-07-15 13:31:51 +0900240 }
Daichi Hirono09109562015-07-28 20:57:05 +0900241 }
Daichi Hironod5152422015-07-15 13:31:51 +0900242
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900243 public void testQueryRoots_error() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900244 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono1d4779c2016-01-06 16:43:32 +0900245 mMtpManager.addValidDevice(new MtpDeviceRecord(
Daichi Hironoebd24052016-02-06 21:05:57 +0900246 0,
247 "Device A",
248 "Device key A",
249 false /* unopened */,
250 new MtpRoot[0],
Daichi Hirono0f325372016-02-21 15:50:30 +0900251 OPERATIONS_SUPPORTED,
Daichi Hironoebd24052016-02-06 21:05:57 +0900252 null));
Daichi Hirono20754c52015-12-15 18:52:26 +0900253 mMtpManager.addValidDevice(new MtpDeviceRecord(
254 1,
Daichi Hironofda74742016-02-01 13:00:31 +0900255 "Device B",
Daichi Hironoebd24052016-02-06 21:05:57 +0900256 "Device key B",
Daichi Hirono20754c52015-12-15 18:52:26 +0900257 false /* unopened */,
258 new MtpRoot[] {
259 new MtpRoot(
260 1 /* deviceId */,
261 1 /* storageId */,
Daichi Hirono20754c52015-12-15 18:52:26 +0900262 "Storage B" /* volume description */,
263 2048 /* free space */,
264 4096 /* total space */,
265 "Identifier B" /* no volume identifier */)
Daichi Hirono1d4779c2016-01-06 16:43:32 +0900266 },
Daichi Hirono0f325372016-02-21 15:50:30 +0900267 OPERATIONS_SUPPORTED,
Daichi Hirono148954a2016-01-21 19:55:45 +0900268 null));
Daichi Hirono09109562015-07-28 20:57:05 +0900269 {
Daichi Hironobeac2182015-07-29 16:28:57 +0900270 mProvider.openDevice(0);
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900271 mResolver.waitForNotification(ROOTS_URI, 1);
272
Daichi Hironofda74742016-02-01 13:00:31 +0900273 mProvider.openDevice(1);
Daichi Hironofda74742016-02-01 13:00:31 +0900274 mResolver.waitForNotification(ROOTS_URI, 2);
275
Daichi Hironobeac2182015-07-29 16:28:57 +0900276 final Cursor cursor = mProvider.queryRoots(null);
Daichi Hirono81d48532015-12-16 15:03:19 +0900277 assertEquals(2, cursor.getCount());
278
279 cursor.moveToNext();
280 assertEquals("1", cursor.getString(0));
Daichi Hirono83c679e72016-08-18 15:13:36 +0900281 assertEquals(
282 Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY,
283 cursor.getInt(1));
Daichi Hirono81d48532015-12-16 15:03:19 +0900284 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
285 assertEquals("Device A", cursor.getString(3));
286 assertEquals("1", cursor.getString(4));
287 assertEquals(0, cursor.getInt(5));
288
Daichi Hirono09109562015-07-28 20:57:05 +0900289 cursor.moveToNext();
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900290 assertEquals("2", cursor.getString(0));
Daichi Hirono83c679e72016-08-18 15:13:36 +0900291 assertEquals(
292 Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY,
293 cursor.getInt(1));
Daichi Hirono39795da2015-12-02 10:56:44 +0900294 assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2));
Daichi Hirono17c8d8b2015-10-12 11:28:46 -0700295 assertEquals("Device B Storage B", cursor.getString(3));
Daichi Hirono6a5ea7e2016-02-02 16:35:03 +0900296 assertEquals("2", cursor.getString(4));
Daichi Hirono09109562015-07-28 20:57:05 +0900297 assertEquals(2048, cursor.getInt(5));
Daichi Hironod5152422015-07-15 13:31:51 +0900298 }
299 }
300
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900301 public void testQueryDocument() throws IOException, InterruptedException, TimeoutException {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900302 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900303 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900304 setupDocuments(
305 0,
306 0,
307 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
308 "1",
309 new MtpObjectInfo[] {
310 new MtpObjectInfo.Builder()
311 .setObjectHandle(100)
312 .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
313 .setName("image.jpg")
314 .setDateModified(1422716400000L)
315 .setCompressedSize(1024 * 1024 * 5)
316 .setThumbCompressedSize(50 * 1024)
317 .build()
318 });
319
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000320 final Cursor cursor = mProvider.queryDocument("3", null);
Daichi Hironoe5323b72015-07-29 16:10:47 +0900321 assertEquals(1, cursor.getCount());
322
323 cursor.moveToNext();
Daichi Hirono47eb1922015-11-16 13:01:31 +0900324
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000325 assertEquals("3", cursor.getString(0));
Daichi Hironoe5323b72015-07-29 16:10:47 +0900326 assertEquals("image/jpeg", cursor.getString(1));
327 assertEquals("image.jpg", cursor.getString(2));
328 assertEquals(1422716400000L, cursor.getLong(3));
329 assertEquals(
330 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900331 DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
Steve McKay5a10ff12017-08-01 15:02:50 -0700332 DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL |
333 DocumentsContract.Document.FLAG_SUPPORTS_METADATA,
Daichi Hironoe5323b72015-07-29 16:10:47 +0900334 cursor.getInt(4));
335 assertEquals(1024 * 1024 * 5, cursor.getInt(5));
336 }
337
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900338 public void testQueryDocument_directory()
339 throws IOException, InterruptedException, TimeoutException {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900340 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900341 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900342 setupDocuments(
343 0,
344 0,
345 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
346 "1",
347 new MtpObjectInfo[] {
348 new MtpObjectInfo.Builder()
349 .setObjectHandle(2)
350 .setStorageId(1)
351 .setFormat(MtpConstants.FORMAT_ASSOCIATION)
352 .setName("directory")
353 .setDateModified(1422716400000L)
354 .build()
355 });
356
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000357 final Cursor cursor = mProvider.queryDocument("3", null);
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900358 assertEquals(1, cursor.getCount());
359
360 cursor.moveToNext();
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000361 assertEquals("3", cursor.getString(0));
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900362 assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
363 assertEquals("directory", cursor.getString(2));
364 assertEquals(1422716400000L, cursor.getLong(3));
365 assertEquals(
366 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
Tomasz Mikolajewskib80a3cf2015-08-24 16:10:51 +0900367 DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE,
368 cursor.getInt(4));
369 assertEquals(0, cursor.getInt(5));
370 }
371
Daichi Hirono66fcb4b2017-03-23 15:24:13 +0900372 public void testQueryDocument_forStorage()
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900373 throws IOException, InterruptedException, TimeoutException {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900374 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900375 setupRoots(0, new MtpRoot[] {
Daichi Hironoe5323b72015-07-29 16:10:47 +0900376 new MtpRoot(
Daichi Hirono8b9024f2015-08-12 12:59:09 +0900377 0 /* deviceId */,
Daichi Hironoe5323b72015-07-29 16:10:47 +0900378 1 /* storageId */,
379 "Storage A" /* volume description */,
380 1024 /* free space */,
381 4096 /* total space */,
382 "" /* no volume identifier */)
383 });
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000384 final Cursor cursor = mProvider.queryDocument("2", null);
Daichi Hironoe5323b72015-07-29 16:10:47 +0900385 assertEquals(1, cursor.getCount());
386
387 cursor.moveToNext();
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000388 assertEquals("2", cursor.getString(0));
Daichi Hironoe5323b72015-07-29 16:10:47 +0900389 assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900390 assertEquals("Storage A", cursor.getString(2));
Daichi Hironoe5323b72015-07-29 16:10:47 +0900391 assertTrue(cursor.isNull(3));
Daichi Hirono4e4ec162017-03-22 15:57:48 +0900392 assertEquals(DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE, cursor.getInt(4));
Daichi Hironoe5323b72015-07-29 16:10:47 +0900393 assertEquals(3072, cursor.getInt(5));
394 }
395
Daichi Hirono66fcb4b2017-03-23 15:24:13 +0900396 public void testQueryDocument_forDeviceWithSingleStorage()
397 throws IOException, InterruptedException, TimeoutException {
398 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
399 setupRoots(0, new MtpRoot[] {
400 new MtpRoot(
401 0 /* deviceId */,
402 1 /* storageId */,
403 "Storage A" /* volume description */,
404 1024 /* free space */,
405 4096 /* total space */,
406 "" /* no volume identifier */)
407 });
408 final Cursor cursor = mProvider.queryDocument("1", null);
409 assertEquals(1, cursor.getCount());
410
411 cursor.moveToNext();
412 assertEquals("1", cursor.getString(0));
413 assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
414 assertEquals("Device Storage A", cursor.getString(2));
415 assertTrue(cursor.isNull(3));
416 assertEquals(DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE, cursor.getInt(4));
417 assertTrue(cursor.isNull(5));
418 }
419
420 public void testQueryDocument_forDeviceWithTwoStorages()
421 throws IOException, InterruptedException, TimeoutException {
422 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
423 setupRoots(0, new MtpRoot[] {
424 new MtpRoot(
425 0 /* deviceId */,
426 1 /* storageId */,
427 "Storage A" /* volume description */,
428 1024 /* free space */,
429 4096 /* total space */,
430 "" /* no volume identifier */),
431 new MtpRoot(
432 0 /* deviceId */,
433 2 /* storageId */,
434 "Storage B" /* volume description */,
435 1024 /* free space */,
436 4096 /* total space */,
437 "" /* no volume identifier */)
438 });
439 final Cursor cursor = mProvider.queryDocument("1", null);
440 assertEquals(1, cursor.getCount());
441
442 cursor.moveToNext();
443 assertEquals("1", cursor.getString(0));
444 assertEquals(DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(1));
445 assertEquals("Device", cursor.getString(2));
446 assertTrue(cursor.isNull(3));
447 assertEquals(0, cursor.getInt(4));
448 assertTrue(cursor.isNull(5));
449 }
450
Daichi Hirono124d0602015-08-11 17:08:35 +0900451 public void testQueryChildDocuments() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900452 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900453 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900454 setupDocuments(
455 0,
456 0,
457 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
458 "1",
459 new MtpObjectInfo[] {
460 new MtpObjectInfo.Builder()
461 .setObjectHandle(100)
462 .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
463 .setName("image.jpg")
464 .setCompressedSize(1024 * 1024 * 5)
465 .setThumbCompressedSize(5 * 1024)
466 .setProtectionStatus(MtpConstants.PROTECTION_STATUS_READ_ONLY)
467 .build()
468 });
Daichi Hirono47eb1922015-11-16 13:01:31 +0900469
Steve McKay29c3f682016-12-16 14:52:59 -0800470 final Cursor cursor = mProvider.queryChildDocuments("1", null, (String) null);
Daichi Hirono124d0602015-08-11 17:08:35 +0900471 assertEquals(1, cursor.getCount());
472
473 assertTrue(cursor.moveToNext());
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000474 assertEquals("3", cursor.getString(0));
Daichi Hirono124d0602015-08-11 17:08:35 +0900475 assertEquals("image/jpeg", cursor.getString(1));
476 assertEquals("image.jpg", cursor.getString(2));
477 assertEquals(0, cursor.getLong(3));
Steve McKay5a10ff12017-08-01 15:02:50 -0700478 assertEquals(Document.FLAG_SUPPORTS_THUMBNAIL
479 | Document.FLAG_SUPPORTS_METADATA, cursor.getInt(4));
Daichi Hirono124d0602015-08-11 17:08:35 +0900480 assertEquals(1024 * 1024 * 5, cursor.getInt(5));
481
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900482 cursor.close();
Daichi Hirono124d0602015-08-11 17:08:35 +0900483 }
484
485 public void testQueryChildDocuments_cursorError() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900486 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono124d0602015-08-11 17:08:35 +0900487 try {
Steve McKay29c3f682016-12-16 14:52:59 -0800488 mProvider.queryChildDocuments("1", null, (String) null);
Daichi Hirono124d0602015-08-11 17:08:35 +0900489 fail();
Daichi Hirono678ed362016-03-18 15:05:53 +0900490 } catch (FileNotFoundException error) {}
Daichi Hirono124d0602015-08-11 17:08:35 +0900491 }
492
493 public void testQueryChildDocuments_documentError() throws Exception {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900494 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900495 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
Daichi Hirono124d0602015-08-11 17:08:35 +0900496 mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
Steve McKay29c3f682016-12-16 14:52:59 -0800497 try (final Cursor cursor = mProvider.queryChildDocuments("1", null, (String) null)) {
Daichi Hirono678ed362016-03-18 15:05:53 +0900498 assertEquals(0, cursor.getCount());
499 assertFalse(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
Daichi Hirono124d0602015-08-11 17:08:35 +0900500 }
501 }
502
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900503 public void testDeleteDocument() throws IOException, InterruptedException, TimeoutException {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900504 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900505 setupRoots(0, new MtpRoot[] {
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900506 new MtpRoot(0, 0, "Storage", 0, 0, "")
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900507 });
508 setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
509 new MtpObjectInfo.Builder()
510 .setName("test.txt")
511 .setObjectHandle(1)
512 .setParent(-1)
513 .build()
514 });
515
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000516 mProvider.deleteDocument("3");
Daichi Hirono5fecc6c2015-08-04 17:45:51 +0900517 assertEquals(1, mResolver.getChangeCount(
518 DocumentsContract.buildChildDocumentsUri(
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900519 MtpDocumentsProvider.AUTHORITY, "1")));
Daichi Hirono5fecc6c2015-08-04 17:45:51 +0900520 }
521
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900522 public void testDeleteDocument_error()
523 throws IOException, InterruptedException, TimeoutException {
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900524 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900525 setupRoots(0, new MtpRoot[] {
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900526 new MtpRoot(0, 0, "Storage", 0, 0, "")
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900527 });
528 setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
529 new MtpObjectInfo.Builder()
530 .setName("test.txt")
531 .setObjectHandle(1)
532 .setParent(-1)
533 .build()
534 });
Daichi Hirono5fecc6c2015-08-04 17:45:51 +0900535 try {
Daichi Hironob3fe72b2015-12-15 07:45:06 +0000536 mProvider.deleteDocument("4");
Daichi Hirono5fecc6c2015-08-04 17:45:51 +0900537 fail();
538 } catch (Throwable e) {
539 assertTrue(e instanceof IOException);
540 }
541 assertEquals(0, mResolver.getChangeCount(
542 DocumentsContract.buildChildDocumentsUri(
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900543 MtpDocumentsProvider.AUTHORITY, "1")));
544 }
545
Daichi Hironob36b1552016-01-25 14:26:14 +0900546 public void testOpenDocument() throws Exception {
547 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
548 setupRoots(0, new MtpRoot[] {
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900549 new MtpRoot(0, 0, "Storage", 0, 0, "")
Daichi Hironob36b1552016-01-25 14:26:14 +0900550 });
551 final byte[] bytes = "Hello world".getBytes();
552 setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
553 new MtpObjectInfo.Builder()
554 .setName("test.txt")
555 .setObjectHandle(1)
556 .setCompressedSize(bytes.length)
557 .setParent(-1)
558 .build()
559 });
560 mMtpManager.setImportFileBytes(0, 1, bytes);
561 try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) {
562 final byte[] readBytes = new byte[5];
563 assertEquals(6, Os.lseek(fd.getFileDescriptor(), 6, OsConstants.SEEK_SET));
564 assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5));
565 assertTrue(Arrays.equals("world".getBytes(), readBytes));
566
567 assertEquals(0, Os.lseek(fd.getFileDescriptor(), 0, OsConstants.SEEK_SET));
568 assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5));
569 assertTrue(Arrays.equals("Hello".getBytes(), readBytes));
570 }
571 }
572
573 public void testOpenDocument_shortBytes() throws Exception {
574 mMtpManager = new TestMtpManager(getContext()) {
575 @Override
576 MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
577 if (objectHandle == 1) {
578 return new MtpObjectInfo.Builder(super.getObjectInfo(deviceId, objectHandle))
579 .setObjectHandle(1).setCompressedSize(1024 * 1024).build();
580 }
581
582 return super.getObjectInfo(deviceId, objectHandle);
583 }
584 };
585 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
586 setupRoots(0, new MtpRoot[] {
Daichi Hironof83ccbd2016-02-04 16:58:55 +0900587 new MtpRoot(0, 0, "Storage", 0, 0, "")
Daichi Hironob36b1552016-01-25 14:26:14 +0900588 });
589 final byte[] bytes = "Hello world".getBytes();
590 setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] {
591 new MtpObjectInfo.Builder()
592 .setName("test.txt")
593 .setObjectHandle(1)
594 .setCompressedSize(bytes.length)
595 .setParent(-1)
596 .build()
597 });
598 mMtpManager.setImportFileBytes(0, 1, bytes);
599 try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) {
600 final byte[] readBytes = new byte[1024 * 1024];
601 assertEquals(11, Os.read(fd.getFileDescriptor(), readBytes, 0, readBytes.length));
602 }
603 }
604
Daichi Hironof4e7fa82016-03-28 16:07:45 +0900605 public void testOpenDocument_writing() throws Exception {
606 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
607 setupRoots(0, new MtpRoot[] {
Daichi Hirono35b2ec52016-11-02 14:51:26 +0900608 new MtpRoot(0, 100, "Storage", 0, 0, "")
Daichi Hironof4e7fa82016-03-28 16:07:45 +0900609 });
610 final String documentId = mProvider.createDocument("2", "text/plain", "test.txt");
611 {
612 final ParcelFileDescriptor fd = mProvider.openDocument(documentId, "w", null);
613 try (ParcelFileDescriptor.AutoCloseOutputStream stream =
614 new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
615 stream.write("Hello".getBytes());
Daichi Hironoe80ea382016-11-15 13:07:01 +0900616 fd.getFileDescriptor().sync();
Daichi Hironof4e7fa82016-03-28 16:07:45 +0900617 }
618 }
619 {
620 final ParcelFileDescriptor fd = mProvider.openDocument(documentId, "r", null);
621 try (ParcelFileDescriptor.AutoCloseInputStream stream =
622 new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
623 final byte[] bytes = new byte[5];
624 stream.read(bytes);
625 assertTrue(Arrays.equals("Hello".getBytes(), bytes));
626 }
627 }
628 }
629
Daichi Hironoc18f8072016-02-10 14:59:52 -0800630 public void testBusyDevice() throws Exception {
631 mMtpManager = new TestMtpManager(getContext()) {
632 @Override
Daichi Hironof20e49ec2016-06-08 16:20:00 +0900633 synchronized MtpDeviceRecord openDevice(int deviceId)
634 throws IOException {
Daichi Hironoc18f8072016-02-10 14:59:52 -0800635 throw new BusyDeviceException();
636 }
637 };
638 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
639 mMtpManager.addValidDevice(new MtpDeviceRecord(
Daichi Hirono0f325372016-02-21 15:50:30 +0900640 0, "Device A", null /* deviceKey */, false /* unopened */, new MtpRoot[0],
641 OPERATIONS_SUPPORTED, null));
Daichi Hironoc18f8072016-02-10 14:59:52 -0800642
643 mProvider.resumeRootScanner();
644 mResolver.waitForNotification(ROOTS_URI, 1);
645
646 try (final Cursor cursor = mProvider.queryRoots(null)) {
647 assertEquals(1, cursor.getCount());
648 }
649
Steve McKay29c3f682016-12-16 14:52:59 -0800650 try (final Cursor cursor = mProvider.queryChildDocuments("1", null, (String) null)) {
Daichi Hironoc18f8072016-02-10 14:59:52 -0800651 assertEquals(0, cursor.getCount());
652 assertEquals(
653 "error_busy_device",
654 cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
655 }
656 }
657
Daichi Hirono29657762016-02-10 16:55:37 -0800658 public void testLockedDevice() throws Exception {
659 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
660 mMtpManager.addValidDevice(new MtpDeviceRecord(
Daichi Hirono0f325372016-02-21 15:50:30 +0900661 0, "Device A", null, false /* unopened */, new MtpRoot[0], OPERATIONS_SUPPORTED,
662 null));
Daichi Hirono29657762016-02-10 16:55:37 -0800663
664 mProvider.resumeRootScanner();
665 mResolver.waitForNotification(ROOTS_URI, 1);
666
667 try (final Cursor cursor = mProvider.queryRoots(null)) {
668 assertEquals(1, cursor.getCount());
669 }
670
Steve McKay29c3f682016-12-16 14:52:59 -0800671 try (final Cursor cursor = mProvider.queryChildDocuments("1", null, (String) null)) {
Daichi Hirono29657762016-02-10 16:55:37 -0800672 assertEquals(0, cursor.getCount());
673 assertEquals(
674 "error_locked_device",
675 cursor.getExtras().getString(DocumentsContract.EXTRA_ERROR));
676 }
677 }
678
Daichi Hirono4e94b8d2016-02-21 22:42:41 +0900679 public void testMappingDisconnectedDocuments() throws Exception {
680 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
681 mMtpManager.addValidDevice(new MtpDeviceRecord(
682 0,
683 "Device A",
684 "device key",
Daichi Hirono37a655a2016-03-04 18:43:21 +0900685 true /* opened */,
Daichi Hirono4e94b8d2016-02-21 22:42:41 +0900686 new MtpRoot[] {
687 new MtpRoot(
688 0 /* deviceId */,
689 1 /* storageId */,
690 "Storage A" /* volume description */,
691 1024 /* free space */,
692 2048 /* total space */,
693 "" /* no volume identifier */)
694 },
Daichi Hirono37a655a2016-03-04 18:43:21 +0900695 OPERATIONS_SUPPORTED,
Daichi Hirono4e94b8d2016-02-21 22:42:41 +0900696 null));
697
698 final String[] names = strings("Directory A", "Directory B", "Directory C");
699 final int objectHandleOffset = 100;
700 for (int i = 0; i < names.length; i++) {
701 final int parentHandle = i == 0 ?
702 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN : objectHandleOffset + i - 1;
703 final int objectHandle = i + objectHandleOffset;
704 mMtpManager.setObjectHandles(0, 1, parentHandle, new int[] { objectHandle });
705 mMtpManager.setObjectInfo(
706 0,
707 new MtpObjectInfo.Builder()
708 .setName(names[i])
709 .setObjectHandle(objectHandle)
710 .setFormat(MtpConstants.FORMAT_ASSOCIATION)
711 .setStorageId(1)
712 .build());
713 }
714
715 mProvider.resumeRootScanner();
716 mResolver.waitForNotification(ROOTS_URI, 1);
717
718 final int documentIdOffset = 2;
719 for (int i = 0; i < names.length; i++) {
720 try (final Cursor cursor = mProvider.queryChildDocuments(
721 String.valueOf(documentIdOffset + i),
722 strings(Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME),
Steve McKay29c3f682016-12-16 14:52:59 -0800723 (String) null)) {
Daichi Hirono4e94b8d2016-02-21 22:42:41 +0900724 assertEquals(1, cursor.getCount());
725 cursor.moveToNext();
726 assertEquals(String.valueOf(documentIdOffset + i + 1), cursor.getString(0));
727 assertEquals(names[i], cursor.getString(1));
728 }
729 }
730
731 mProvider.closeDevice(0);
732 mResolver.waitForNotification(ROOTS_URI, 2);
733
734 mProvider.openDevice(0);
735 mResolver.waitForNotification(ROOTS_URI, 3);
736
737 for (int i = 0; i < names.length; i++) {
738 mResolver.waitForNotification(DocumentsContract.buildChildDocumentsUri(
739 MtpDocumentsProvider.AUTHORITY,
740 String.valueOf(documentIdOffset + i)), 1);
741 try (final Cursor cursor = mProvider.queryChildDocuments(
742 String.valueOf(documentIdOffset + i),
743 strings(Document.COLUMN_DOCUMENT_ID),
Steve McKay29c3f682016-12-16 14:52:59 -0800744 (String) null)) {
Daichi Hirono4e94b8d2016-02-21 22:42:41 +0900745 assertEquals(1, cursor.getCount());
746 cursor.moveToNext();
747 assertEquals(String.valueOf(documentIdOffset + i + 1), cursor.getString(0));
748 }
749 }
750 }
751
Daichi Hirono35b2ec52016-11-02 14:51:26 +0900752 public void testCreateDocument() throws Exception {
753 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
754 setupRoots(0, new MtpRoot[] {
755 new MtpRoot(0, 100, "Storage A", 100, 100, null)
756 });
757 final String documentId = mProvider.createDocument("1", "text/plain", "note.txt");
758 final Uri deviceUri = DocumentsContract.buildChildDocumentsUri(
759 MtpDocumentsProvider.AUTHORITY, "1");
760 final Uri storageUri = DocumentsContract.buildChildDocumentsUri(
761 MtpDocumentsProvider.AUTHORITY, "2");
762 mResolver.waitForNotification(storageUri, 1);
763 mResolver.waitForNotification(deviceUri, 1);
764 try (final Cursor cursor = mProvider.queryDocument(documentId, null)) {
765 assertTrue(cursor.moveToNext());
766 assertEquals(
767 "note.txt",
768 cursor.getString(cursor.getColumnIndex(Document.COLUMN_DISPLAY_NAME)));
769 assertEquals(
770 "text/plain",
771 cursor.getString(cursor.getColumnIndex(Document.COLUMN_MIME_TYPE)));
772 }
773 }
774
Daichi Hirono0f325372016-02-21 15:50:30 +0900775 public void testCreateDocument_noWritingSupport() throws Exception {
776 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
777 mMtpManager.addValidDevice(new MtpDeviceRecord(
778 0, "Device A", null /* deviceKey */, false /* unopened */,
779 new MtpRoot[] {
780 new MtpRoot(
781 0 /* deviceId */,
782 1 /* storageId */,
783 "Storage A" /* volume description */,
784 1024 /* free space */,
785 2048 /* total space */,
786 "" /* no volume identifier */)
787 },
788 new int[0] /* no operations supported */, null));
789 mProvider.resumeRootScanner();
790 mResolver.waitForNotification(ROOTS_URI, 1);
791 try {
792 mProvider.createDocument("1", "text/palin", "note.txt");
793 fail();
794 } catch (UnsupportedOperationException exception) {}
795 }
796
797 public void testOpenDocument_noWritingSupport() throws Exception {
798 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
799 mMtpManager.addValidDevice(new MtpDeviceRecord(
800 0, "Device A", null /* deviceKey */, false /* unopened */,
801 new MtpRoot[] {
802 new MtpRoot(
803 0 /* deviceId */,
804 1 /* storageId */,
805 "Storage A" /* volume description */,
806 1024 /* free space */,
807 2048 /* total space */,
808 "" /* no volume identifier */)
809 },
810 new int[0] /* no operations supported */, null));
811 mMtpManager.setObjectHandles(
812 0, 1, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, new int[] { 100 });
813 mMtpManager.setObjectInfo(
814 0, new MtpObjectInfo.Builder().setObjectHandle(100).setName("note.txt").build());
815 mProvider.resumeRootScanner();
816 mResolver.waitForNotification(ROOTS_URI, 1);
817 try (final Cursor cursor = mProvider.queryChildDocuments(
Steve McKay29c3f682016-12-16 14:52:59 -0800818 "1", strings(Document.COLUMN_DOCUMENT_ID), (String) null)) {
Daichi Hirono0f325372016-02-21 15:50:30 +0900819 assertEquals(1, cursor.getCount());
820 cursor.moveToNext();
821 assertEquals("3", cursor.getString(0));
822 }
823 try {
824 mProvider.openDocument("3", "w", null);
825 fail();
826 } catch (UnsupportedOperationException exception) {}
827 }
828
Daichi Hirono64111e02016-03-24 21:07:38 +0900829 public void testObjectSizeLong() throws Exception {
830 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
831 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
832 mMtpManager.setObjectSizeLong(0, 100, MtpConstants.FORMAT_EXIF_JPEG, 0x400000000L);
833 setupDocuments(
834 0,
835 0,
836 MtpManager.OBJECT_HANDLE_ROOT_CHILDREN,
837 "1",
838 new MtpObjectInfo[] {
839 new MtpObjectInfo.Builder()
840 .setObjectHandle(100)
841 .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
842 .setName("image.jpg")
843 .setCompressedSize(0xffffffffl)
844 .build()
845 });
846
847 final Cursor cursor = mProvider.queryDocument("3", new String[] {
848 DocumentsContract.Document.COLUMN_SIZE
849 });
850 assertEquals(1, cursor.getCount());
851
852 cursor.moveToNext();
853 assertEquals(0x400000000L, cursor.getLong(0));
854 }
855
Garfield Tanf46a4632016-11-02 16:00:17 -0700856 public void testFindDocumentPath_singleStorage_toRoot() throws Exception {
Daichi Hironob9ffa2a2016-11-01 18:41:43 +0900857 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
858 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
859 setupHierarchyDocuments("1");
860
Garfield Tanb690b4d2017-03-01 16:05:23 -0800861 final Path path = mProvider.findDocumentPath(null, "15");
Daichi Hironob9ffa2a2016-11-01 18:41:43 +0900862 assertEquals("1", path.getRootId());
863 assertEquals(4, path.getPath().size());
864 assertEquals("1", path.getPath().get(0));
865 assertEquals("3", path.getPath().get(1));
866 assertEquals("6", path.getPath().get(2));
867 assertEquals("15", path.getPath().get(3));
868 }
869
Garfield Tanf46a4632016-11-02 16:00:17 -0700870 public void testFindDocumentPath_singleStorage_toDoc() throws Exception {
Daichi Hironob9ffa2a2016-11-01 18:41:43 +0900871 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
872 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
873 setupHierarchyDocuments("1");
874
Garfield Tanb690b4d2017-03-01 16:05:23 -0800875 final Path path = mProvider.findDocumentPath("3", "18");
Daichi Hironob9ffa2a2016-11-01 18:41:43 +0900876 assertNull(path.getRootId());
877 assertEquals(3, path.getPath().size());
878 assertEquals("3", path.getPath().get(0));
879 assertEquals("7", path.getPath().get(1));
880 assertEquals("18", path.getPath().get(2));
881 }
882
Garfield Tanf46a4632016-11-02 16:00:17 -0700883 public void testFindDocumentPath_multiStorage_toRoot() throws Exception {
Daichi Hironob9ffa2a2016-11-01 18:41:43 +0900884 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
885 setupRoots(0, new MtpRoot[] {
886 new MtpRoot(0, 0, "Storage A", 1000, 1000, ""),
887 new MtpRoot(0, 1, "Storage B", 1000, 1000, "") });
888 setupHierarchyDocuments("2");
889
Garfield Tanb690b4d2017-03-01 16:05:23 -0800890 final Path path = mProvider.findDocumentPath(null, "16");
Daichi Hironob9ffa2a2016-11-01 18:41:43 +0900891 assertEquals("2", path.getRootId());
892 assertEquals(4, path.getPath().size());
893 assertEquals("2", path.getPath().get(0));
894 assertEquals("4", path.getPath().get(1));
895 assertEquals("7", path.getPath().get(2));
896 assertEquals("16", path.getPath().get(3));
897 }
898
Garfield Tanf46a4632016-11-02 16:00:17 -0700899 public void testFindDocumentPath_multiStorage_toDoc() throws Exception {
Daichi Hironob9ffa2a2016-11-01 18:41:43 +0900900 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
901 setupRoots(0, new MtpRoot[] {
902 new MtpRoot(0, 0, "Storage A", 1000, 1000, ""),
903 new MtpRoot(0, 1, "Storage B", 1000, 1000, "") });
904 setupHierarchyDocuments("2");
905
Garfield Tanb690b4d2017-03-01 16:05:23 -0800906 final Path path = mProvider.findDocumentPath("4", "19");
Daichi Hironob9ffa2a2016-11-01 18:41:43 +0900907 assertNull(path.getRootId());
908 assertEquals(3, path.getPath().size());
909 assertEquals("4", path.getPath().get(0));
910 assertEquals("8", path.getPath().get(1));
911 assertEquals("19", path.getPath().get(2));
912 }
913
Daichi Hirono29de7692016-11-07 10:58:50 +0900914 public void testIsChildDocument() throws Exception {
915 setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
916 setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
917 setupHierarchyDocuments("1");
918 assertTrue(mProvider.isChildDocument("1", "1"));
919 assertTrue(mProvider.isChildDocument("1", "14"));
920 assertTrue(mProvider.isChildDocument("2", "14"));
921 assertTrue(mProvider.isChildDocument("5", "14"));
922 assertFalse(mProvider.isChildDocument("3", "14"));
923 assertFalse(mProvider.isChildDocument("6", "14"));
924 }
925
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900926 private void setupProvider(int flag) {
927 mDatabase = new MtpDatabase(getContext(), flag);
928 mProvider = new MtpDocumentsProvider();
Daichi Hironob36b1552016-01-25 14:26:14 +0900929 final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
930 assertTrue(mProvider.onCreateForTesting(
Daichi Hironof4e7fa82016-03-28 16:07:45 +0900931 getContext(),
Daichi Hironofda74742016-02-01 13:00:31 +0900932 mResources,
933 mMtpManager,
934 mResolver,
935 mDatabase,
936 storageManager,
937 new TestServiceIntentSender()));
Daichi Hironoe0282dd2015-11-26 15:20:08 +0900938 }
939
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900940 private String[] getStrings(Cursor cursor) {
941 try {
942 final String[] results = new String[cursor.getCount()];
943 for (int i = 0; cursor.moveToNext(); i++) {
944 results[i] = cursor.getString(0);
945 }
946 return results;
947 } finally {
948 cursor.close();
949 }
950 }
951
952 private String[] setupRoots(int deviceId, MtpRoot[] roots)
Daichi Hirono20754c52015-12-15 18:52:26 +0900953 throws InterruptedException, TimeoutException, IOException {
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900954 final int changeCount = mResolver.getChangeCount(ROOTS_URI);
Daichi Hirono20754c52015-12-15 18:52:26 +0900955 mMtpManager.addValidDevice(
Daichi Hironoebd24052016-02-06 21:05:57 +0900956 new MtpDeviceRecord(deviceId, "Device", null /* deviceKey */, false /* unopened */,
Daichi Hirono0f325372016-02-21 15:50:30 +0900957 roots, OPERATIONS_SUPPORTED, null));
Daichi Hirono20754c52015-12-15 18:52:26 +0900958 mProvider.openDevice(deviceId);
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900959 mResolver.waitForNotification(ROOTS_URI, changeCount + 1);
960 return getStrings(mProvider.queryRoots(strings(DocumentsContract.Root.COLUMN_ROOT_ID)));
961 }
962
963 private String[] setupDocuments(
964 int deviceId,
965 int storageId,
966 int parentHandle,
967 String parentDocumentId,
968 MtpObjectInfo[] objects) throws FileNotFoundException {
969 final int[] handles = new int[objects.length];
970 int i = 0;
971 for (final MtpObjectInfo info : objects) {
Daichi Hironob9ffa2a2016-11-01 18:41:43 +0900972 handles[i++] = info.getObjectHandle();
Daichi Hirono9e8a4fa2015-11-19 16:13:38 +0900973 mMtpManager.setObjectInfo(deviceId, info);
974 }
975 mMtpManager.setObjectHandles(deviceId, storageId, parentHandle, handles);
976 return getStrings(mProvider.queryChildDocuments(
Steve McKay29c3f682016-12-16 14:52:59 -0800977 parentDocumentId,
978 strings(DocumentsContract.Document.COLUMN_DOCUMENT_ID),
979 (String) null));
Daichi Hirono5fecc6c2015-08-04 17:45:51 +0900980 }
Daichi Hironob9ffa2a2016-11-01 18:41:43 +0900981
982 static class HierarchyDocument {
983 int depth;
984 String documentId;
985 int objectHandle;
986 int parentHandle;
987
988 HierarchyDocument createChildDocument(int newHandle) {
989 final HierarchyDocument doc = new HierarchyDocument();
990 doc.depth = depth - 1;
991 doc.objectHandle = newHandle;
992 doc.parentHandle = objectHandle;
993 return doc;
994 }
995
996 MtpObjectInfo toObjectInfo() {
997 return new MtpObjectInfo.Builder()
998 .setName("doc_" + documentId)
999 .setFormat(depth > 0 ?
1000 MtpConstants.FORMAT_ASSOCIATION : MtpConstants.FORMAT_TEXT)
1001 .setObjectHandle(objectHandle)
1002 .setParent(parentHandle)
1003 .build();
1004 }
1005 }
1006
1007 private void setupHierarchyDocuments(String documentId) throws Exception {
1008 final Queue<HierarchyDocument> ids = new LinkedList<>();
1009 final HierarchyDocument firstDocument = new HierarchyDocument();
1010 firstDocument.depth = 3;
1011 firstDocument.documentId = documentId;
1012 firstDocument.objectHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN;
1013 ids.add(firstDocument);
1014
1015 int objectHandle = 100;
1016 while (!ids.isEmpty()) {
1017 final HierarchyDocument document = ids.remove();
1018 final HierarchyDocument[] children = new HierarchyDocument[] {
1019 document.createChildDocument(objectHandle++),
1020 document.createChildDocument(objectHandle++),
1021 document.createChildDocument(objectHandle++),
1022 };
1023 final String[] childDocIds = setupDocuments(
1024 0, 0, document.objectHandle, document.documentId, new MtpObjectInfo[] {
1025 children[0].toObjectInfo(),
1026 children[1].toObjectInfo(),
1027 children[2].toObjectInfo(),
1028 });
1029 children[0].documentId = childDocIds[0];
1030 children[1].documentId = childDocIds[1];
1031 children[2].documentId = childDocIds[2];
1032
1033 if (children[0].depth > 0) {
1034 ids.add(children[0]);
1035 ids.add(children[1]);
1036 ids.add(children[2]);
1037 }
1038 }
1039 }
Daichi Hirono9a05d6b2015-07-09 15:36:42 +09001040}