1 /*
2  * Copyright (C) 2009 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.server;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.content.ContextWrapper;
22 import android.content.Intent;
23 import android.os.DropBoxManager;
24 import android.os.Looper;
25 import android.os.Parcel;
26 import android.os.ParcelFileDescriptor;
27 import android.os.Parcelable;
28 import android.os.Process;
29 import android.os.StatFs;
30 import android.os.UserHandle;
31 import android.provider.Settings;
32 import android.test.AndroidTestCase;
33 
34 import com.android.server.DropBoxManagerInternal.EntrySource;
35 import com.android.server.DropBoxManagerService.EntryFile;
36 
37 import libcore.io.Streams;
38 
39 import java.io.BufferedReader;
40 import java.io.File;
41 import java.io.FileDescriptor;
42 import java.io.FileOutputStream;
43 import java.io.FileWriter;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.io.InputStreamReader;
47 import java.nio.charset.StandardCharsets;
48 import java.util.Random;
49 import java.util.zip.GZIPOutputStream;
50 
51 /**
52  * Test {@link DropBoxManager} functionality.
53  *
54  * Run with:
55  * bit FrameworksServicesTests:com.android.server.DropBoxTest
56  */
57 public class DropBoxTest extends AndroidTestCase {
58     private Context mContext;
59 
60     @Override
setUp()61     protected void setUp() throws Exception {
62         super.setUp();
63 
64         LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
65 
66         mContext = new ContextWrapper(super.getContext()) {
67             @Override
68             public void sendBroadcastAsUser(Intent intent,
69                     UserHandle user, String receiverPermission) {
70                 // Don't actually send broadcasts.
71             }
72         };
73     }
74 
tearDown()75     public void tearDown() throws Exception {
76         ContentResolver cr = getContext().getContentResolver();
77         Settings.Global.putString(cr, Settings.Global.DROPBOX_AGE_SECONDS, "");
78         Settings.Global.putString(cr, Settings.Global.DROPBOX_MAX_FILES, "");
79         Settings.Global.putString(cr, Settings.Global.DROPBOX_QUOTA_KB, "");
80         Settings.Global.putString(cr, Settings.Global.DROPBOX_TAG_PREFIX + "DropBoxTest", "");
81     }
82 
83     @Override
getContext()84     public Context getContext() {
85         return mContext;
86     }
87 
testAddText()88     public void testAddText() throws Exception {
89         File dir = getEmptyDir("testAddText");
90         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
91                 Looper.getMainLooper());
92         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
93 
94         long before = System.currentTimeMillis();
95         Thread.sleep(5);
96         dropbox.addText("DropBoxTest", "TEST0");
97         Thread.sleep(5);
98         long between = System.currentTimeMillis();
99         Thread.sleep(5);
100         dropbox.addText("DropBoxTest", "TEST1");
101         dropbox.addText("DropBoxTest", "TEST2");
102         Thread.sleep(5);
103         long after = System.currentTimeMillis();
104 
105         DropBoxManager.Entry e0 = dropbox.getNextEntry("DropBoxTest", before);
106         DropBoxManager.Entry e1 = dropbox.getNextEntry("DropBoxTest", e0.getTimeMillis());
107         DropBoxManager.Entry e2 = dropbox.getNextEntry("DropBoxTest", e1.getTimeMillis());
108         assertTrue(null == dropbox.getNextEntry("DropBoxTest", e2.getTimeMillis()));
109 
110         assertTrue(e0.getTimeMillis() > before);
111         assertTrue(e0.getTimeMillis() < between);
112         assertTrue(e1.getTimeMillis() > between);
113         assertTrue(e1.getTimeMillis() < e2.getTimeMillis());
114         assertTrue(e2.getTimeMillis() < after);
115 
116         assertEquals("TEST0", e0.getText(80));
117         assertEquals("TEST1", e1.getText(80));
118         assertEquals("TES", e2.getText(3));
119 
120         e0.close();
121         e1.close();
122         e2.close();
123     }
124 
125     public void testAddData() throws Exception {
126         File dir = getEmptyDir("testAddData");
127         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
128                 Looper.getMainLooper());
129         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
130 
131         long before = System.currentTimeMillis();
132         Thread.sleep(1);
133         dropbox.addData("DropBoxTest", "TEST".getBytes(), 0);
134         Thread.sleep(1);
135         long after = System.currentTimeMillis();
136 
137         DropBoxManager.Entry e = dropbox.getNextEntry("DropBoxTest", before);
138         assertNotNull(e);
139         assertNull(dropbox.getNextEntry("DropBoxTest", e.getTimeMillis()));
140 
141         assertEquals("DropBoxTest", e.getTag());
142         assertTrue(e.getTimeMillis() >= before);
143         assertEquals(0, e.getFlags());
144         assertTrue(null == e.getText(80));
145 
146         byte[] buf = new byte[80];
147         assertEquals("TEST", new String(buf, 0, e.getInputStream().read(buf)));
148 
149         e.close();
150     }
151 
152     public void testAddFile() throws Exception {
153         File dir = getEmptyDir("testAddFile");
154         long before = System.currentTimeMillis();
155 
156         File clientDir = getEmptyDir("testAddFile_client");
157 
158         File f0 = new File(clientDir, "f0.txt");
159         File f1 = new File(clientDir, "f1.txt.gz");
160         File f2 = new File(clientDir, "f2.dat");
161         File f3 = new File(clientDir, "f2.dat.gz");
162 
163         FileWriter w0 = new FileWriter(f0);
164         GZIPOutputStream gz1 = new GZIPOutputStream(new FileOutputStream(f1));
165         FileOutputStream os2 = new FileOutputStream(f2);
166         GZIPOutputStream gz3 = new GZIPOutputStream(new FileOutputStream(f3));
167 
168         w0.write("FILE0");
169         gz1.write("FILE1".getBytes());
170         os2.write("DATA2".getBytes());
171         gz3.write("DATA3".getBytes());
172 
173         w0.close();
174         gz1.close();
175         os2.close();
176         gz3.close();
177 
178         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
179                 Looper.getMainLooper());
180         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
181 
182         dropbox.addFile("DropBoxTest", f0, DropBoxManager.IS_TEXT);
183         dropbox.addFile("DropBoxTest", f1, DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED);
184         dropbox.addFile("DropBoxTest", f2, 0);
185         dropbox.addFile("DropBoxTest", f3, DropBoxManager.IS_GZIPPED);
186 
187         DropBoxManager.Entry e0 = dropbox.getNextEntry("DropBoxTest", before);
188         DropBoxManager.Entry e1 = dropbox.getNextEntry("DropBoxTest", e0.getTimeMillis());
189         DropBoxManager.Entry e2 = dropbox.getNextEntry("DropBoxTest", e1.getTimeMillis());
190         DropBoxManager.Entry e3 = dropbox.getNextEntry("DropBoxTest", e2.getTimeMillis());
191         assertTrue(null == dropbox.getNextEntry("DropBoxTest", e3.getTimeMillis()));
192 
193         assertTrue(e0.getTimeMillis() > before);
194         assertTrue(e1.getTimeMillis() > e0.getTimeMillis());
195         assertTrue(e2.getTimeMillis() > e1.getTimeMillis());
196         assertTrue(e3.getTimeMillis() > e2.getTimeMillis());
197 
198         assertEquals(DropBoxManager.IS_TEXT, e0.getFlags());
199         assertEquals(DropBoxManager.IS_TEXT, e1.getFlags());
200         assertEquals(0, e2.getFlags());
201         assertEquals(0, e3.getFlags());
202 
203         assertEquals("FILE0", e0.getText(80));
204 
205         byte[] buf1 = new byte[80];
206         assertEquals("FILE1", new String(buf1, 0, e1.getInputStream().read(buf1)));
207 
208         assertTrue(null == e2.getText(80));
209         byte[] buf2 = new byte[80];
210         assertEquals("DATA2", new String(buf2, 0, e2.getInputStream().read(buf2)));
211 
212         assertTrue(null == e3.getText(80));
213         byte[] buf3 = new byte[80];
214         assertEquals("DATA3", new String(buf3, 0, e3.getInputStream().read(buf3)));
215 
216         e0.close();
217         e1.close();
218         e2.close();
219         e3.close();
220     }
221 
testAddEntry_Success()222     public void testAddEntry_Success() throws Exception {
223         File dir = getEmptyDir("testAddEntry");
224         long before = System.currentTimeMillis();
225 
226         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
227                 Looper.getMainLooper());
228         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
229 
230         LocalServices.getService(DropBoxManagerInternal.class).addEntry("DropBoxTest",
231                 new EntrySource() {
232                     @Override
233                     public void writeTo(FileDescriptor fd) throws IOException {
234                         try (FileOutputStream out = new FileOutputStream(fd)) {
235                             out.write("test".getBytes(StandardCharsets.UTF_8));
236                         }
237                     }
238 
239                     @Override
240                     public void close() throws IOException {
241                     }
242 
243                     @Override
244                     public long length() {
245                         return 0;
246                     }
247                 }, DropBoxManager.IS_TEXT);
248 
249         DropBoxManager.Entry entry = dropbox.getNextEntry("DropBoxTest", before);
250         assertEquals(DropBoxManager.IS_TEXT, entry.getFlags());
251         assertEquals("test", new String(Streams.readFully(entry.getInputStream())));
252     }
253 
testAddEntry_Failure()254     public void testAddEntry_Failure() throws Exception {
255         File dir = getEmptyDir("testAddEntry");
256         long before = System.currentTimeMillis();
257 
258         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
259                 Looper.getMainLooper());
260         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
261 
262         LocalServices.getService(DropBoxManagerInternal.class).addEntry("DropBoxTest",
263                 new EntrySource() {
264                     @Override
265                     public void writeTo(FileDescriptor fd) throws IOException {
266                         throw new IOException();
267                     }
268 
269                     @Override
270                     public void close() throws IOException {
271                     }
272 
273                     @Override
274                     public long length() {
275                         return 0;
276                     }
277                 }, DropBoxManager.IS_TEXT);
278 
279         DropBoxManager.Entry entry = dropbox.getNextEntry("DropBoxTest", before);
280         assertNull(entry);
281     }
282 
testAddEntriesInTheFuture()283     public void testAddEntriesInTheFuture() throws Exception {
284         File dir = getEmptyDir("testAddEntriesInTheFuture");
285         long before = System.currentTimeMillis();
286 
287         // Near future: should be allowed to persist
288         FileWriter w0 = new FileWriter(new File(dir, "DropBoxTest@" + (before + 5000) + ".txt"));
289         w0.write("FUTURE0");
290         w0.close();
291 
292         // Far future: should be collapsed
293         FileWriter w1 = new FileWriter(new File(dir, "DropBoxTest@" + (before + 100000) + ".txt"));
294         w1.write("FUTURE1");
295         w1.close();
296 
297         // Another far future item, this one gzipped
298         File f2 = new File(dir, "DropBoxTest@" + (before + 100001) + ".txt.gz");
299         GZIPOutputStream gz2 = new GZIPOutputStream(new FileOutputStream(f2));
300         gz2.write("FUTURE2".getBytes());
301         gz2.close();
302 
303         // Tombstone in the far future
304         new FileOutputStream(new File(dir, "DropBoxTest@" + (before + 100002) + ".lost")).close();
305 
306         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
307                 Looper.getMainLooper());
308         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
309 
310         // Until a write, the timestamps are taken at face value
311         DropBoxManager.Entry e0 = dropbox.getNextEntry(null, before);
312         DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
313         DropBoxManager.Entry e2 = dropbox.getNextEntry(null, e1.getTimeMillis());
314         DropBoxManager.Entry e3 = dropbox.getNextEntry(null, e2.getTimeMillis());
315         assertTrue(null == dropbox.getNextEntry(null, e3.getTimeMillis()));
316 
317         assertEquals("FUTURE0", e0.getText(80));
318         assertEquals("FUTURE1", e1.getText(80));
319         assertEquals("FUTURE2", e2.getText(80));
320         assertEquals(null, e3.getText(80));
321 
322         assertEquals(before + 5000, e0.getTimeMillis());
323         assertEquals(before + 100000, e1.getTimeMillis());
324         assertEquals(before + 100001, e2.getTimeMillis());
325         assertEquals(before + 100002, e3.getTimeMillis());
326 
327         e0.close();
328         e1.close();
329         e2.close();
330         e3.close();
331 
332         // Write something to force a collapse
333         dropbox.addText("NotDropBoxTest", "FUTURE");
334         e0 = dropbox.getNextEntry(null, before);
335         e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
336         e2 = dropbox.getNextEntry(null, e1.getTimeMillis());
337         e3 = dropbox.getNextEntry(null, e2.getTimeMillis());
338         assertTrue(null == dropbox.getNextEntry("DropBoxTest", e3.getTimeMillis()));
339 
340         assertEquals("FUTURE0", e0.getText(80));
341         assertEquals("FUTURE1", e1.getText(80));
342         assertEquals("FUTURE2", e2.getText(80));
343         assertEquals(null, e3.getText(80));
344 
345         assertEquals(before + 5000, e0.getTimeMillis());
346         assertEquals(before + 5001, e1.getTimeMillis());
347         assertEquals(before + 5002, e2.getTimeMillis());
348         assertEquals(before + 5003, e3.getTimeMillis());
349 
350         e0.close();
351         e1.close();
352         e2.close();
353         e3.close();
354     }
355 
testIsTagEnabled()356     public void testIsTagEnabled() throws Exception {
357         File dir = getEmptyDir("testIsTagEnabled");
358         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
359                 Looper.getMainLooper());
360         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
361 
362         long before = System.currentTimeMillis();
363         dropbox.addText("DropBoxTest", "TEST-ENABLED");
364         assertTrue(dropbox.isTagEnabled("DropBoxTest"));
365 
366         ContentResolver cr = getContext().getContentResolver();
367         Settings.Global.putString(cr, Settings.Global.DROPBOX_TAG_PREFIX + "DropBoxTest",
368                                   "disabled");
369 
370         dropbox.addText("DropBoxTest", "TEST-DISABLED");
371         assertFalse(dropbox.isTagEnabled("DropBoxTest"));
372 
373         Settings.Global.putString(cr, Settings.Global.DROPBOX_TAG_PREFIX + "DropBoxTest",
374                                   "");
375 
376         dropbox.addText("DropBoxTest", "TEST-ENABLED-AGAIN");
377         assertTrue(dropbox.isTagEnabled("DropBoxTest"));
378 
379         DropBoxManager.Entry e0 = dropbox.getNextEntry("DropBoxTest", before);
380         DropBoxManager.Entry e1 = dropbox.getNextEntry("DropBoxTest", e0.getTimeMillis());
381         assertTrue(null == dropbox.getNextEntry("DropBoxTest", e1.getTimeMillis()));
382 
383         assertEquals("TEST-ENABLED", e0.getText(80));
384         assertEquals("TEST-ENABLED-AGAIN", e1.getText(80));
385 
386         e0.close();
387         e1.close();
388     }
389 
testGetNextEntry()390     public void testGetNextEntry() throws Exception {
391         File dir = getEmptyDir("testGetNextEntry");
392         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
393                 Looper.getMainLooper());
394         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
395 
396         long before = System.currentTimeMillis();
397         dropbox.addText("DropBoxTest.A", "A0");
398         dropbox.addText("DropBoxTest.B", "B0");
399         dropbox.addText("DropBoxTest.A", "A1");
400 
401         DropBoxManager.Entry a0 = dropbox.getNextEntry("DropBoxTest.A", before);
402         DropBoxManager.Entry a1 = dropbox.getNextEntry("DropBoxTest.A", a0.getTimeMillis());
403         assertTrue(null == dropbox.getNextEntry("DropBoxTest.A", a1.getTimeMillis()));
404 
405         DropBoxManager.Entry b0 = dropbox.getNextEntry("DropBoxTest.B", before);
406         assertTrue(null == dropbox.getNextEntry("DropBoxTest.B", b0.getTimeMillis()));
407 
408         DropBoxManager.Entry x0 = dropbox.getNextEntry(null, before);
409         DropBoxManager.Entry x1 = dropbox.getNextEntry(null, x0.getTimeMillis());
410         DropBoxManager.Entry x2 = dropbox.getNextEntry(null, x1.getTimeMillis());
411         assertTrue(null == dropbox.getNextEntry(null, x2.getTimeMillis()));
412 
413         assertEquals("DropBoxTest.A", a0.getTag());
414         assertEquals("DropBoxTest.A", a1.getTag());
415         assertEquals("A0", a0.getText(80));
416         assertEquals("A1", a1.getText(80));
417 
418         assertEquals("DropBoxTest.B", b0.getTag());
419         assertEquals("B0", b0.getText(80));
420 
421         assertEquals("DropBoxTest.A", x0.getTag());
422         assertEquals("DropBoxTest.B", x1.getTag());
423         assertEquals("DropBoxTest.A", x2.getTag());
424         assertEquals("A0", x0.getText(80));
425         assertEquals("B0", x1.getText(80));
426         assertEquals("A1", x2.getText(80));
427 
428         a0.close();
429         a1.close();
430         b0.close();
431         x0.close();
432         x1.close();
433         x2.close();
434     }
435 
testSizeLimits()436     public void testSizeLimits() throws Exception {
437         File dir = getEmptyDir("testSizeLimits");
438         int blockSize =  new StatFs(dir.getPath()).getBlockSize();
439 
440         // Limit storage to 10 blocks
441         int kb = blockSize * 10 / 1024;
442         ContentResolver cr = getContext().getContentResolver();
443         Settings.Global.putString(cr, Settings.Global.DROPBOX_QUOTA_KB, Integer.toString(kb));
444 
445         // Three tags using a total of 12 blocks:
446         // DropBoxTest0 [ ][ ]
447         // DropBoxTest1 [x][ ][    ][ ][xxx(20 blocks)xxx]
448         // DropBoxTest2 [xxxxxxxxxx][ ][ ]
449         //
450         // The blocks marked "x" will be removed due to storage restrictions.
451         // Use random fill (so it doesn't compress), subtract a little for gzip overhead
452 
453         final int overhead = 64;
454         long before = System.currentTimeMillis();
455         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
456                 Looper.getMainLooper());
457         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
458 
459         addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead);
460         addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead);
461 
462         addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead);
463         addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead);
464         addRandomEntry(dropbox, "DropBoxTest1", blockSize * 2 - overhead);
465         addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead);
466         addRandomEntry(dropbox, "DropBoxTest1", blockSize * 20 - overhead);
467 
468         addRandomEntry(dropbox, "DropBoxTest2", blockSize * 4 - overhead);
469         addRandomEntry(dropbox, "DropBoxTest2", blockSize - overhead);
470         addRandomEntry(dropbox, "DropBoxTest2", blockSize - overhead);
471 
472         DropBoxManager.Entry e0 = dropbox.getNextEntry(null, before);
473         DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
474         DropBoxManager.Entry e2 = dropbox.getNextEntry(null, e1.getTimeMillis());
475         DropBoxManager.Entry e3 = dropbox.getNextEntry(null, e2.getTimeMillis());
476         DropBoxManager.Entry e4 = dropbox.getNextEntry(null, e3.getTimeMillis());
477         DropBoxManager.Entry e5 = dropbox.getNextEntry(null, e4.getTimeMillis());
478         DropBoxManager.Entry e6 = dropbox.getNextEntry(null, e5.getTimeMillis());
479         DropBoxManager.Entry e7 = dropbox.getNextEntry(null, e6.getTimeMillis());
480         DropBoxManager.Entry e8 = dropbox.getNextEntry(null, e7.getTimeMillis());
481         DropBoxManager.Entry e9 = dropbox.getNextEntry(null, e8.getTimeMillis());
482         assertTrue(null == dropbox.getNextEntry(null, e9.getTimeMillis()));
483 
484         assertEquals("DropBoxTest0", e0.getTag());
485         assertEquals("DropBoxTest0", e1.getTag());
486         assertEquals(blockSize - overhead, getEntrySize(e0));
487         assertEquals(blockSize - overhead, getEntrySize(e1));
488 
489         assertEquals("DropBoxTest1", e2.getTag());
490         assertEquals("DropBoxTest1", e3.getTag());
491         assertEquals("DropBoxTest1", e4.getTag());
492         assertEquals("DropBoxTest1", e5.getTag());
493         assertEquals("DropBoxTest1", e6.getTag());
494         assertEquals(-1, getEntrySize(e2));  // Tombstone
495         assertEquals(blockSize - overhead, getEntrySize(e3));
496         assertEquals(blockSize * 2 - overhead, getEntrySize(e4));
497         assertEquals(blockSize - overhead, getEntrySize(e5));
498         assertEquals(-1, getEntrySize(e6));
499 
500         assertEquals("DropBoxTest2", e7.getTag());
501         assertEquals("DropBoxTest2", e8.getTag());
502         assertEquals("DropBoxTest2", e9.getTag());
503         assertEquals(-1, getEntrySize(e7));  // Tombstone
504         assertEquals(blockSize - overhead, getEntrySize(e8));
505         assertEquals(blockSize - overhead, getEntrySize(e9));
506 
507         e0.close();
508         e1.close();
509         e2.close();
510         e3.close();
511         e4.close();
512         e5.close();
513         e6.close();
514         e7.close();
515         e8.close();
516         e9.close();
517 
518         // Specifying a tag name skips tombstone records.
519 
520         DropBoxManager.Entry t0 = dropbox.getNextEntry("DropBoxTest1", before);
521         DropBoxManager.Entry t1 = dropbox.getNextEntry("DropBoxTest1", t0.getTimeMillis());
522         DropBoxManager.Entry t2 = dropbox.getNextEntry("DropBoxTest1", t1.getTimeMillis());
523         assertTrue(null == dropbox.getNextEntry("DropBoxTest1", t2.getTimeMillis()));
524 
525         assertEquals("DropBoxTest1", t0.getTag());
526         assertEquals("DropBoxTest1", t1.getTag());
527         assertEquals("DropBoxTest1", t2.getTag());
528 
529         assertEquals(blockSize - overhead, getEntrySize(t0));
530         assertEquals(blockSize * 2 - overhead, getEntrySize(t1));
531         assertEquals(blockSize - overhead, getEntrySize(t2));
532 
533         t0.close();
534         t1.close();
535         t2.close();
536     }
537 
testAgeLimits()538     public void testAgeLimits() throws Exception {
539         File dir = getEmptyDir("testAgeLimits");
540         int blockSize = new StatFs(dir.getPath()).getBlockSize();
541 
542         // Limit storage to 10 blocks with an expiration of 1 second
543         int kb = blockSize * 10 / 1024;
544         ContentResolver cr = getContext().getContentResolver();
545         Settings.Global.putString(cr, Settings.Global.DROPBOX_AGE_SECONDS, "1");
546         Settings.Global.putString(cr, Settings.Global.DROPBOX_QUOTA_KB, Integer.toString(kb));
547 
548         // Write one normal entry and another so big that it is instantly tombstoned
549         long before = System.currentTimeMillis();
550         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
551                 Looper.getMainLooper());
552         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
553 
554         dropbox.addText("DropBoxTest", "TEST");
555         addRandomEntry(dropbox, "DropBoxTest", blockSize * 20);
556 
557         // Verify that things are as expected
558         DropBoxManager.Entry e0 = dropbox.getNextEntry(null, before);
559         DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
560         assertTrue(null == dropbox.getNextEntry(null, e1.getTimeMillis()));
561 
562         assertEquals("TEST", e0.getText(80));
563         assertEquals(null, e1.getText(80));
564         assertEquals(-1, getEntrySize(e1));
565 
566         e0.close();
567         e1.close();
568 
569         // Wait a second and write another entry -- old ones should be expunged
570         Thread.sleep(2000);
571         dropbox.addText("DropBoxTest", "TEST1");
572 
573         e0 = dropbox.getNextEntry(null, before);
574         assertTrue(null == dropbox.getNextEntry(null, e0.getTimeMillis()));
575         assertEquals("TEST1", e0.getText(80));
576         e0.close();
577     }
578 
testFileCountLimits()579     public void testFileCountLimits() throws Exception {
580         File dir = getEmptyDir("testFileCountLimits");
581 
582         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
583                 Looper.getMainLooper());
584         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
585         dropbox.addText("DropBoxTest", "TEST0");
586         dropbox.addText("DropBoxTest", "TEST1");
587         dropbox.addText("DropBoxTest", "TEST2");
588         dropbox.addText("DropBoxTest", "TEST3");
589         dropbox.addText("DropBoxTest", "TEST4");
590         dropbox.addText("DropBoxTest", "TEST5");
591 
592         // Verify 6 files added
593         DropBoxManager.Entry e0 = dropbox.getNextEntry(null, 0);
594         DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
595         DropBoxManager.Entry e2 = dropbox.getNextEntry(null, e1.getTimeMillis());
596         DropBoxManager.Entry e3 = dropbox.getNextEntry(null, e2.getTimeMillis());
597         DropBoxManager.Entry e4 = dropbox.getNextEntry(null, e3.getTimeMillis());
598         DropBoxManager.Entry e5 = dropbox.getNextEntry(null, e4.getTimeMillis());
599         assertTrue(null == dropbox.getNextEntry(null, e5.getTimeMillis()));
600         assertEquals("TEST0", e0.getText(80));
601         assertEquals("TEST5", e5.getText(80));
602 
603         e0.close();
604         e1.close();
605         e2.close();
606         e3.close();
607         e4.close();
608         e5.close();
609 
610         // Limit to 3 files and add one more entry
611         ContentResolver cr = getContext().getContentResolver();
612         Settings.Global.putString(cr, Settings.Global.DROPBOX_MAX_FILES, "3");
613         dropbox.addText("DropBoxTest", "TEST6");
614 
615         // Verify only 3 files left
616         DropBoxManager.Entry f0 = dropbox.getNextEntry(null, 0);
617         DropBoxManager.Entry f1 = dropbox.getNextEntry(null, f0.getTimeMillis());
618         DropBoxManager.Entry f2 = dropbox.getNextEntry(null, f1.getTimeMillis());
619         assertTrue(null == dropbox.getNextEntry(null, f2.getTimeMillis()));
620         assertEquals("TEST4", f0.getText(80));
621         assertEquals("TEST5", f1.getText(80));
622         assertEquals("TEST6", f2.getText(80));
623 
624         f0.close();
625         f1.close();
626         f2.close();
627     }
628 
testCreateDropBoxManagerWithInvalidDirectory()629     public void testCreateDropBoxManagerWithInvalidDirectory() throws Exception {
630         // If created with an invalid directory, the DropBoxManager should suffer quietly
631         // and fail all operations (this is how it survives a full disk).
632         // Once the directory becomes possible to create, it will start working.
633 
634         File dir = new File(getEmptyDir("testCreateDropBoxManagerWith"), "InvalidDirectory");
635         new FileOutputStream(dir).close();  // Create an empty file
636         DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
637                 Looper.getMainLooper());
638         DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
639 
640         dropbox.addText("DropBoxTest", "should be ignored");
641         dropbox.addData("DropBoxTest", "should be ignored".getBytes(), 0);
642         assertTrue(null == dropbox.getNextEntry("DropBoxTest", 0));
643 
644         dir.delete();  // Remove the file so a directory can be created
645         dropbox.addText("DropBoxTest", "TEST");
646         DropBoxManager.Entry e = dropbox.getNextEntry("DropBoxTest", 0);
647         assertTrue(null == dropbox.getNextEntry("DropBoxTest", e.getTimeMillis()));
648         assertEquals("DropBoxTest", e.getTag());
649         assertEquals("TEST", e.getText(80));
650         e.close();
651     }
652 
testDropBoxEntrySerialization()653     public void testDropBoxEntrySerialization() throws Exception {
654         // Make sure DropBoxManager.Entry can be serialized to a Parcel and back
655         // under a variety of conditions.
656 
657         Parcel parcel = Parcel.obtain();
658         File dir = getEmptyDir("testDropBoxEntrySerialization");
659 
660         new DropBoxManager.Entry("empty", 1000000).writeToParcel(parcel, 0);
661         new DropBoxManager.Entry("string", 2000000, "String Value").writeToParcel(parcel, 0);
662         new DropBoxManager.Entry("bytes", 3000000, "Bytes Value".getBytes(),
663                 DropBoxManager.IS_TEXT).writeToParcel(parcel, 0);
664         new DropBoxManager.Entry("zerobytes", 4000000, new byte[0], 0).writeToParcel(parcel, 0);
665         new DropBoxManager.Entry("emptybytes", 5000000, (byte[]) null,
666                 DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
667 
668         try {
669             new DropBoxManager.Entry("badbytes", 99999,
670                     "Bad Bytes Value".getBytes(),
671                     DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
672             fail("IllegalArgumentException expected for non-null byte[] and IS_EMPTY flags");
673         } catch (IllegalArgumentException e) {
674             // expected
675         }
676 
677         try {
678             new DropBoxManager.Entry("badbytes", 99999, (byte[]) null, 0).writeToParcel(parcel, 0);
679             fail("IllegalArgumentException expected for null byte[] and non-IS_EMPTY flags");
680         } catch (IllegalArgumentException e) {
681             // expected
682         }
683 
684         File f = new File(dir, "file.dat");
685         FileOutputStream os = new FileOutputStream(f);
686         os.write("File Value".getBytes());
687         os.close();
688 
689         new DropBoxManager.Entry("file", 6000000, f, DropBoxManager.IS_TEXT).writeToParcel(
690                 parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
691         new DropBoxManager.Entry("binfile", 7000000, f, 0).writeToParcel(
692                 parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
693         new DropBoxManager.Entry("emptyfile", 8000000, (ParcelFileDescriptor) null,
694                 DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
695 
696         try {
697             new DropBoxManager.Entry("badfile", 99999, new File(dir, "nonexist.dat"), 0);
698             fail("IOException expected for nonexistent file");
699         } catch (IOException e) {
700             // expected
701         }
702 
703         try {
704             new DropBoxManager.Entry("badfile", 99999, f, DropBoxManager.IS_EMPTY).writeToParcel(
705                     parcel, 0);
706             fail("IllegalArgumentException expected for non-null file and IS_EMPTY flags");
707         } catch (IllegalArgumentException e) {
708             // expected
709         }
710 
711         try {
712             new DropBoxManager.Entry("badfile", 99999, (ParcelFileDescriptor) null, 0);
713             fail("IllegalArgumentException expected for null PFD and non-IS_EMPTY flags");
714         } catch (IllegalArgumentException e) {
715             // expected
716         }
717 
718         File gz = new File(dir, "file.gz");
719         GZIPOutputStream gzout = new GZIPOutputStream(new FileOutputStream(gz));
720         gzout.write("Gzip File Value".getBytes());
721         gzout.close();
722 
723         new DropBoxManager.Entry("gzipfile", 9000000, gz,
724                 DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED).writeToParcel(
725                     parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
726         new DropBoxManager.Entry("gzipbinfile", 10000000, gz,
727                 DropBoxManager.IS_GZIPPED).writeToParcel(
728                     parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
729 
730         //
731         // Switch from writing to reading
732         //
733 
734         parcel.setDataPosition(0);
735         DropBoxManager.Entry e;
736 
737         e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
738         assertEquals("empty", e.getTag());
739         assertEquals(1000000, e.getTimeMillis());
740         assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
741         assertEquals(null, e.getText(100));
742         assertEquals(null, e.getInputStream());
743         e.close();
744 
745         e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
746         assertEquals("string", e.getTag());
747         assertEquals(2000000, e.getTimeMillis());
748         assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
749         assertEquals("String Value", e.getText(100));
750         assertEquals("String Value",
751                 new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
752         e.close();
753 
754         e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
755         assertEquals("bytes", e.getTag());
756         assertEquals(3000000, e.getTimeMillis());
757         assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
758         assertEquals("Bytes Value", e.getText(100));
759         e.close();
760 
761         e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
762         assertEquals("zerobytes", e.getTag());
763         assertEquals(4000000, e.getTimeMillis());
764         assertEquals(0, e.getFlags());
765         assertEquals(null, e.getText(100));
766         assertEquals(null,
767                 new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
768         e.close();
769 
770         e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
771         assertEquals("emptybytes", e.getTag());
772         assertEquals(5000000, e.getTimeMillis());
773         assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
774         assertEquals(null, e.getText(100));
775         assertEquals(null, e.getInputStream());
776         e.close();
777 
778         e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
779         assertEquals("file", e.getTag());
780         assertEquals(6000000, e.getTimeMillis());
781         assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
782         assertEquals("File Value", e.getText(100));
783         e.close();
784 
785         e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
786         assertEquals("binfile", e.getTag());
787         assertEquals(7000000, e.getTimeMillis());
788         assertEquals(0, e.getFlags());
789         assertEquals(null, e.getText(100));
790         assertEquals("File Value",
791                 new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
792         e.close();
793         e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
794         assertEquals("emptyfile", e.getTag());
795         assertEquals(8000000, e.getTimeMillis());
796         assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
797         assertEquals(null, e.getText(100));
798         assertEquals(null, e.getInputStream());
799         e.close();
800 
801         e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
802         assertEquals("gzipfile", e.getTag());
803         assertEquals(9000000, e.getTimeMillis());
804         assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
805         assertEquals("Gzip File Value", e.getText(100));
806         e.close();
807 
808         e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
809         assertEquals("gzipbinfile", e.getTag());
810         assertEquals(10000000, e.getTimeMillis());
811         assertEquals(0, e.getFlags());
812         assertEquals(null, e.getText(100));
813         assertEquals("Gzip File Value",
814                 new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
815         e.close();
816         assertEquals(0, parcel.dataAvail());
817         parcel.recycle();
818     }
819 
testDropBoxEntrySerializationDoesntLeakFileDescriptors()820     public void testDropBoxEntrySerializationDoesntLeakFileDescriptors() throws Exception {
821         File dir = getEmptyDir("testDropBoxEntrySerialization");
822         File f = new File(dir, "file.dat");
823         FileOutputStream os = new FileOutputStream(f);
824         os.write("File Value".getBytes());
825         os.close();
826 
827         int before = countOpenFiles();
828         assertTrue(before > 0);
829 
830         for (int i = 0; i < 1000; i++) {
831             Parcel parcel = Parcel.obtain();
832             new DropBoxManager.Entry("file", 1000000, f, 0).writeToParcel(
833                     parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
834 
835             parcel.setDataPosition(0);
836             DropBoxManager.Entry e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
837             assertEquals("file", e.getTag());
838             e.close();
839 
840             parcel.recycle();
841         }
842 
843         int after = countOpenFiles();
844         assertTrue(after > 0);
845         assertTrue(after < before + 20);
846     }
847 
848     public void testEntryFile() throws Exception {
849         File fromDir = getEmptyDir("testEntryFile_from");
850         File toDir = getEmptyDir("testEntryFile_to");
851 
852         {
853             File f = new File(fromDir, "f0.txt");
854             try (FileWriter w = new FileWriter(f)) {
855                 w.write("abc");
856             }
857 
858             EntryFile e = new EntryFile(f, toDir, "tag:!", 12345, DropBoxManager.IS_TEXT, 1024);
859 
860             assertEquals("tag:!", e.tag);
861             assertEquals(12345, e.timestampMillis);
862             assertEquals(DropBoxManager.IS_TEXT, e.flags);
863             assertEquals(1, e.blocks);
864 
865             assertFalse(f.exists()); // Because it should be renamed.
866 
867             assertTrue(e.hasFile());
868             assertEquals(new File(toDir, "tag%3A!@12345.txt"), e.getFile(toDir));
869             assertTrue(e.getFile(toDir).exists());
870         }
871         // Same test with gzip.
872         {
873             File f = new File(fromDir, "f0.txt.gz"); // It's a lie; it's not actually gz.
874             try (FileWriter w = new FileWriter(f)) {
875                 w.write("abc");
876             }
877 
878             EntryFile e = new EntryFile(f, toDir, "tag:!", 12345,
879                     DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED, 1024);
880 
881             assertEquals("tag:!", e.tag);
882 
883             assertFalse(f.exists()); // Because it should be renamed.
884 
885             assertTrue(e.hasFile());
886             assertEquals(new File(toDir, "tag%3A!@12345.txt.gz"), e.getFile(toDir));
887             assertTrue(e.getFile(toDir).exists());
888 
889         }
890         // binary, gzip.
891         {
892             File f = new File(fromDir, "f0.dat.gz"); // It's a lie; it's not actually gz.
893             try (FileWriter w = new FileWriter(f)) {
894                 w.write("abc");
895             }
896 
897             EntryFile e = new EntryFile(f, toDir, "tag:!", 12345,
898                     DropBoxManager.IS_GZIPPED, 1024);
899 
900             assertEquals("tag:!", e.tag);
901 
902             assertFalse(f.exists()); // Because it should be renamed.
903 
904             assertTrue(e.hasFile());
905             assertEquals(new File(toDir, "tag%3A!@12345.dat.gz"), e.getFile(toDir));
906             assertTrue(e.getFile(toDir).exists());
907 
908         }
909 
910         // Tombstone.
911         {
912             EntryFile e = new EntryFile(toDir, "tag:!", 12345);
913 
914             assertEquals("tag:!", e.tag);
915             assertEquals(12345, e.timestampMillis);
916             assertEquals(DropBoxManager.IS_EMPTY, e.flags);
917             assertEquals(0, e.blocks);
918 
919             assertTrue(e.hasFile());
920             assertEquals(new File(toDir, "tag%3A!@12345.lost"), e.getFile(toDir));
921             assertTrue(e.getFile(toDir).exists());
922         }
923 
924         // From existing files.
925         {
926             File f = new File(fromDir, "tag%3A!@12345.dat");
927             f.createNewFile();
928 
929             EntryFile e = new EntryFile(f, 1024);
930 
931             assertEquals("tag:!", e.tag);
932             assertEquals(12345, e.timestampMillis);
933             assertEquals(0, e.flags);
934             assertEquals(0, e.blocks);
935 
936             assertTrue(f.exists());
937         }
938         {
939             File f = new File(fromDir, "tag%3A!@12345.dat.gz");
940             f.createNewFile();
941 
942             EntryFile e = new EntryFile(f, 1024);
943 
944             assertEquals("tag:!", e.tag);
945             assertEquals(12345, e.timestampMillis);
946             assertEquals(DropBoxManager.IS_GZIPPED, e.flags);
947             assertEquals(0, e.blocks);
948 
949             assertTrue(f.exists());
950         }
951         {
952             File f = new File(fromDir, "tag%3A!@12345.txt");
953             try (FileWriter w = new FileWriter(f)) {
954                 w.write(new char[1024]);
955             }
956 
957             EntryFile e = new EntryFile(f, 1024);
958 
959             assertEquals("tag:!", e.tag);
960             assertEquals(12345, e.timestampMillis);
961             assertEquals(DropBoxManager.IS_TEXT, e.flags);
962             assertEquals(1, e.blocks);
963 
964             assertTrue(f.exists());
965         }
966         {
967             File f = new File(fromDir, "tag%3A!@12345.txt.gz");
968             try (FileWriter w = new FileWriter(f)) {
969                 w.write(new char[1025]);
970             }
971 
972             EntryFile e = new EntryFile(f, 1024);
973 
974             assertEquals("tag:!", e.tag);
975             assertEquals(12345, e.timestampMillis);
976             assertEquals(DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED, e.flags);
977             assertEquals(2, e.blocks);
978 
979             assertTrue(f.exists());
980         }
981         {
982             File f = new File(fromDir, "tag%3A!@12345.lost");
983             f.createNewFile();
984 
985             EntryFile e = new EntryFile(f, 1024);
986 
987             assertEquals("tag:!", e.tag);
988             assertEquals(12345, e.timestampMillis);
989             assertEquals(DropBoxManager.IS_EMPTY, e.flags);
990             assertEquals(0, e.blocks);
991 
992             assertTrue(f.exists());
993         }
994         {
995             File f = new File(fromDir, "@12345.dat"); // Empty tag -- this actually works.
996             f.createNewFile();
997 
998             EntryFile e = new EntryFile(f, 1024);
999 
1000             assertEquals("", e.tag);
1001             assertEquals(12345, e.timestampMillis);
1002             assertEquals(0, e.flags);
1003             assertEquals(0, e.blocks);
1004 
1005             assertTrue(f.exists());
1006         }
1007         // From invalid filenames.
1008         {
1009             File f = new File(fromDir, "tag.dat"); // No @.
1010             f.createNewFile();
1011 
1012             EntryFile e = new EntryFile(f, 1024);
1013 
1014             assertEquals(null, e.tag);
1015             assertEquals(0, e.timestampMillis);
1016             assertEquals(DropBoxManager.IS_EMPTY, e.flags);
1017             assertEquals(0, e.blocks);
1018 
1019             assertFalse(f.exists());
1020         }
1021         {
1022             File f = new File(fromDir, "tag@.dat"); // Invalid timestamp.
1023             f.createNewFile();
1024 
1025             EntryFile e = new EntryFile(f, 1024);
1026 
1027             assertEquals(null, e.tag);
1028             assertEquals(0, e.timestampMillis);
1029             assertEquals(DropBoxManager.IS_EMPTY, e.flags);
1030             assertEquals(0, e.blocks);
1031 
1032             assertFalse(f.exists());
1033         }
1034         {
1035             File f = new File(fromDir, "tag@12345.daxt"); // Invalid extension.
1036             f.createNewFile();
1037 
1038             EntryFile e = new EntryFile(f, 1024);
1039 
1040             assertEquals(null, e.tag);
1041             assertEquals(0, e.timestampMillis);
1042             assertEquals(DropBoxManager.IS_EMPTY, e.flags);
1043             assertEquals(0, e.blocks);
1044 
1045             assertFalse(f.exists());
1046         }
1047     }
1048 
1049     public void testCompareEntries() {
1050         File dir = getEmptyDir("testCompareEntries");
1051         assertEquals(-1,
1052                 new EntryFile(new File(dir, "aaa@100.dat"), 1).compareTo(
1053                 new EntryFile(new File(dir, "bbb@200.dat"), 1)));
1054         assertEquals(1,
1055                 new EntryFile(new File(dir, "aaa@200.dat"), 1).compareTo(
1056                 new EntryFile(new File(dir, "bbb@100.dat"), 1)));
1057         assertEquals(-1,
1058                 new EntryFile(new File(dir, "aaa@100.dat"), 1).compareTo(
1059                 new EntryFile(new File(dir, "bbb@100.dat"), 1)));
1060         assertEquals(1,
1061                 new EntryFile(new File(dir, "bbb@100.dat"), 1).compareTo(
1062                 new EntryFile(new File(dir, "aaa@100.dat"), 1)));
1063     }
1064 
1065     private void addRandomEntry(DropBoxManager dropbox, String tag, int size) throws Exception {
1066         byte[] bytes = new byte[size];
1067         new Random(System.currentTimeMillis()).nextBytes(bytes);
1068 
1069         File f = new File(getEmptyDir("addRandomEntry"), "random.dat");
1070         FileOutputStream os = new FileOutputStream(f);
1071         os.write(bytes);
1072         os.close();
1073 
1074         dropbox.addFile(tag, f, 0);
1075     }
1076 
1077     private int getEntrySize(DropBoxManager.Entry e) throws Exception {
1078         InputStream is = e.getInputStream();
1079         if (is == null) return -1;
1080         int length = 0;
1081         while (is.read() != -1) length++;
1082         return length;
1083     }
1084 
1085     private void recursiveDelete(File file) {
1086         if (!file.delete() && file.isDirectory()) {
1087             for (File f : file.listFiles()) recursiveDelete(f);
1088             file.delete();
1089         }
1090     }
1091 
1092     private File getEmptyDir(String name) {
1093         File dir = getContext().getDir("DropBoxTest." + name, 0);
1094         for (File f : dir.listFiles()) recursiveDelete(f);
1095         assertTrue(dir.listFiles().length == 0);
1096         return dir;
1097     }
1098 
1099     private int countOpenFiles() {
1100         return new File("/proc/" + Process.myPid() + "/fd").listFiles().length;
1101     }
1102 }
1103