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
17package com.android.server;
18
19import android.content.ContentResolver;
20import android.content.Context;
21import android.content.Intent;
22import android.os.DropBoxManager;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.os.ParcelFileDescriptor;
26import android.os.Process;
27import android.os.ServiceManager;
28import android.os.StatFs;
29import android.provider.Settings;
30import android.test.AndroidTestCase;
31
32import com.android.server.DropBoxManagerService;
33
34import java.io.BufferedReader;
35import java.io.File;
36import java.io.FileOutputStream;
37import java.io.FileWriter;
38import java.io.IOException;
39import java.io.InputStream;
40import java.io.InputStreamReader;
41import java.util.Random;
42import java.util.zip.GZIPOutputStream;
43
44/** Test {@link DropBoxManager} functionality. */
45public class DropBoxTest extends AndroidTestCase {
46    public void tearDown() throws Exception {
47        ContentResolver cr = getContext().getContentResolver();
48        Settings.Global.putString(cr, Settings.Global.DROPBOX_AGE_SECONDS, "");
49        Settings.Global.putString(cr, Settings.Global.DROPBOX_MAX_FILES, "");
50        Settings.Global.putString(cr, Settings.Global.DROPBOX_QUOTA_KB, "");
51        Settings.Global.putString(cr, Settings.Global.DROPBOX_TAG_PREFIX + "DropBoxTest", "");
52    }
53
54    public void testAddText() throws Exception {
55        File dir = getEmptyDir("testAddText");
56        DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
57        DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
58
59        long before = System.currentTimeMillis();
60        Thread.sleep(5);
61        dropbox.addText("DropBoxTest", "TEST0");
62        Thread.sleep(5);
63        long between = System.currentTimeMillis();
64        Thread.sleep(5);
65        dropbox.addText("DropBoxTest", "TEST1");
66        dropbox.addText("DropBoxTest", "TEST2");
67        Thread.sleep(5);
68        long after = System.currentTimeMillis();
69
70        DropBoxManager.Entry e0 = dropbox.getNextEntry("DropBoxTest", before);
71        DropBoxManager.Entry e1 = dropbox.getNextEntry("DropBoxTest", e0.getTimeMillis());
72        DropBoxManager.Entry e2 = dropbox.getNextEntry("DropBoxTest", e1.getTimeMillis());
73        assertTrue(null == dropbox.getNextEntry("DropBoxTest", e2.getTimeMillis()));
74
75        assertTrue(e0.getTimeMillis() > before);
76        assertTrue(e0.getTimeMillis() < between);
77        assertTrue(e1.getTimeMillis() > between);
78        assertTrue(e1.getTimeMillis() < e2.getTimeMillis());
79        assertTrue(e2.getTimeMillis() < after);
80
81        assertEquals("TEST0", e0.getText(80));
82        assertEquals("TEST1", e1.getText(80));
83        assertEquals("TES", e2.getText(3));
84
85        e0.close();
86        e1.close();
87        e2.close();
88    }
89
90    public void testAddData() throws Exception {
91        File dir = getEmptyDir("testAddData");
92        DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
93        DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
94
95        long before = System.currentTimeMillis();
96        dropbox.addData("DropBoxTest", "TEST".getBytes(), 0);
97        long after = System.currentTimeMillis();
98
99        DropBoxManager.Entry e = dropbox.getNextEntry("DropBoxTest", before);
100        assertTrue(null == dropbox.getNextEntry("DropBoxTest", e.getTimeMillis()));
101
102        assertEquals("DropBoxTest", e.getTag());
103        assertTrue(e.getTimeMillis() >= before);
104        assertEquals(0, e.getFlags());
105        assertTrue(null == e.getText(80));
106
107        byte[] buf = new byte[80];
108        assertEquals("TEST", new String(buf, 0, e.getInputStream().read(buf)));
109
110        e.close();
111    }
112
113    public void testAddFile() throws Exception {
114        File dir = getEmptyDir("testAddFile");
115        long before = System.currentTimeMillis();
116
117        File f0 = new File(dir, "f0.txt");
118        File f1 = new File(dir, "f1.txt.gz");
119        File f2 = new File(dir, "f2.dat");
120        File f3 = new File(dir, "f2.dat.gz");
121
122        FileWriter w0 = new FileWriter(f0);
123        GZIPOutputStream gz1 = new GZIPOutputStream(new FileOutputStream(f1));
124        FileOutputStream os2 = new FileOutputStream(f2);
125        GZIPOutputStream gz3 = new GZIPOutputStream(new FileOutputStream(f3));
126
127        w0.write("FILE0");
128        gz1.write("FILE1".getBytes());
129        os2.write("DATA2".getBytes());
130        gz3.write("DATA3".getBytes());
131
132        w0.close();
133        gz1.close();
134        os2.close();
135        gz3.close();
136
137        DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
138        DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
139
140        dropbox.addFile("DropBoxTest", f0, DropBoxManager.IS_TEXT);
141        dropbox.addFile("DropBoxTest", f1, DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED);
142        dropbox.addFile("DropBoxTest", f2, 0);
143        dropbox.addFile("DropBoxTest", f3, DropBoxManager.IS_GZIPPED);
144
145        DropBoxManager.Entry e0 = dropbox.getNextEntry("DropBoxTest", before);
146        DropBoxManager.Entry e1 = dropbox.getNextEntry("DropBoxTest", e0.getTimeMillis());
147        DropBoxManager.Entry e2 = dropbox.getNextEntry("DropBoxTest", e1.getTimeMillis());
148        DropBoxManager.Entry e3 = dropbox.getNextEntry("DropBoxTest", e2.getTimeMillis());
149        assertTrue(null == dropbox.getNextEntry("DropBoxTest", e3.getTimeMillis()));
150
151        assertTrue(e0.getTimeMillis() > before);
152        assertTrue(e1.getTimeMillis() > e0.getTimeMillis());
153        assertTrue(e2.getTimeMillis() > e1.getTimeMillis());
154        assertTrue(e3.getTimeMillis() > e2.getTimeMillis());
155
156        assertEquals(DropBoxManager.IS_TEXT, e0.getFlags());
157        assertEquals(DropBoxManager.IS_TEXT, e1.getFlags());
158        assertEquals(0, e2.getFlags());
159        assertEquals(0, e3.getFlags());
160
161        assertEquals("FILE0", e0.getText(80));
162
163        byte[] buf1 = new byte[80];
164        assertEquals("FILE1", new String(buf1, 0, e1.getInputStream().read(buf1)));
165
166        assertTrue(null == e2.getText(80));
167        byte[] buf2 = new byte[80];
168        assertEquals("DATA2", new String(buf2, 0, e2.getInputStream().read(buf2)));
169
170        assertTrue(null == e3.getText(80));
171        byte[] buf3 = new byte[80];
172        assertEquals("DATA3", new String(buf3, 0, e3.getInputStream().read(buf3)));
173
174        e0.close();
175        e1.close();
176        e2.close();
177        e3.close();
178    }
179
180    public void testAddEntriesInTheFuture() throws Exception {
181        File dir = getEmptyDir("testAddEntriesInTheFuture");
182        long before = System.currentTimeMillis();
183
184        // Near future: should be allowed to persist
185        FileWriter w0 = new FileWriter(new File(dir, "DropBoxTest@" + (before + 5000) + ".txt"));
186        w0.write("FUTURE0");
187        w0.close();
188
189        // Far future: should be collapsed
190        FileWriter w1 = new FileWriter(new File(dir, "DropBoxTest@" + (before + 100000) + ".txt"));
191        w1.write("FUTURE1");
192        w1.close();
193
194        // Another far future item, this one gzipped
195        File f2 = new File(dir, "DropBoxTest@" + (before + 100001) + ".txt.gz");
196        GZIPOutputStream gz2 = new GZIPOutputStream(new FileOutputStream(f2));
197        gz2.write("FUTURE2".getBytes());
198        gz2.close();
199
200        // Tombstone in the far future
201        new FileOutputStream(new File(dir, "DropBoxTest@" + (before + 100002) + ".lost")).close();
202
203        DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
204        DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
205
206        // Until a write, the timestamps are taken at face value
207        DropBoxManager.Entry e0 = dropbox.getNextEntry(null, before);
208        DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
209        DropBoxManager.Entry e2 = dropbox.getNextEntry(null, e1.getTimeMillis());
210        DropBoxManager.Entry e3 = dropbox.getNextEntry(null, e2.getTimeMillis());
211        assertTrue(null == dropbox.getNextEntry(null, e3.getTimeMillis()));
212
213        assertEquals("FUTURE0", e0.getText(80));
214        assertEquals("FUTURE1", e1.getText(80));
215        assertEquals("FUTURE2", e2.getText(80));
216        assertEquals(null, e3.getText(80));
217
218        assertEquals(before + 5000, e0.getTimeMillis());
219        assertEquals(before + 100000, e1.getTimeMillis());
220        assertEquals(before + 100001, e2.getTimeMillis());
221        assertEquals(before + 100002, e3.getTimeMillis());
222
223        e0.close();
224        e1.close();
225        e2.close();
226        e3.close();
227
228        // Write something to force a collapse
229        dropbox.addText("NotDropBoxTest", "FUTURE");
230        e0 = dropbox.getNextEntry(null, before);
231        e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
232        e2 = dropbox.getNextEntry(null, e1.getTimeMillis());
233        e3 = dropbox.getNextEntry(null, e2.getTimeMillis());
234        assertTrue(null == dropbox.getNextEntry("DropBoxTest", e3.getTimeMillis()));
235
236        assertEquals("FUTURE0", e0.getText(80));
237        assertEquals("FUTURE1", e1.getText(80));
238        assertEquals("FUTURE2", e2.getText(80));
239        assertEquals(null, e3.getText(80));
240
241        assertEquals(before + 5000, e0.getTimeMillis());
242        assertEquals(before + 5001, e1.getTimeMillis());
243        assertEquals(before + 5002, e2.getTimeMillis());
244        assertEquals(before + 5003, e3.getTimeMillis());
245
246        e0.close();
247        e1.close();
248        e2.close();
249        e3.close();
250    }
251
252    public void testIsTagEnabled() throws Exception {
253        File dir = getEmptyDir("testIsTagEnabled");
254        DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
255        DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
256
257        long before = System.currentTimeMillis();
258        dropbox.addText("DropBoxTest", "TEST-ENABLED");
259        assertTrue(dropbox.isTagEnabled("DropBoxTest"));
260
261        ContentResolver cr = getContext().getContentResolver();
262        Settings.Global.putString(cr, Settings.Global.DROPBOX_TAG_PREFIX + "DropBoxTest",
263                                  "disabled");
264
265        dropbox.addText("DropBoxTest", "TEST-DISABLED");
266        assertFalse(dropbox.isTagEnabled("DropBoxTest"));
267
268        Settings.Global.putString(cr, Settings.Global.DROPBOX_TAG_PREFIX + "DropBoxTest",
269                                  "");
270
271        dropbox.addText("DropBoxTest", "TEST-ENABLED-AGAIN");
272        assertTrue(dropbox.isTagEnabled("DropBoxTest"));
273
274        DropBoxManager.Entry e0 = dropbox.getNextEntry("DropBoxTest", before);
275        DropBoxManager.Entry e1 = dropbox.getNextEntry("DropBoxTest", e0.getTimeMillis());
276        assertTrue(null == dropbox.getNextEntry("DropBoxTest", e1.getTimeMillis()));
277
278        assertEquals("TEST-ENABLED", e0.getText(80));
279        assertEquals("TEST-ENABLED-AGAIN", e1.getText(80));
280
281        e0.close();
282        e1.close();
283    }
284
285    public void testGetNextEntry() throws Exception {
286        File dir = getEmptyDir("testGetNextEntry");
287        DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
288        DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
289
290        long before = System.currentTimeMillis();
291        dropbox.addText("DropBoxTest.A", "A0");
292        dropbox.addText("DropBoxTest.B", "B0");
293        dropbox.addText("DropBoxTest.A", "A1");
294
295        DropBoxManager.Entry a0 = dropbox.getNextEntry("DropBoxTest.A", before);
296        DropBoxManager.Entry a1 = dropbox.getNextEntry("DropBoxTest.A", a0.getTimeMillis());
297        assertTrue(null == dropbox.getNextEntry("DropBoxTest.A", a1.getTimeMillis()));
298
299        DropBoxManager.Entry b0 = dropbox.getNextEntry("DropBoxTest.B", before);
300        assertTrue(null == dropbox.getNextEntry("DropBoxTest.B", b0.getTimeMillis()));
301
302        DropBoxManager.Entry x0 = dropbox.getNextEntry(null, before);
303        DropBoxManager.Entry x1 = dropbox.getNextEntry(null, x0.getTimeMillis());
304        DropBoxManager.Entry x2 = dropbox.getNextEntry(null, x1.getTimeMillis());
305        assertTrue(null == dropbox.getNextEntry(null, x2.getTimeMillis()));
306
307        assertEquals("DropBoxTest.A", a0.getTag());
308        assertEquals("DropBoxTest.A", a1.getTag());
309        assertEquals("A0", a0.getText(80));
310        assertEquals("A1", a1.getText(80));
311
312        assertEquals("DropBoxTest.B", b0.getTag());
313        assertEquals("B0", b0.getText(80));
314
315        assertEquals("DropBoxTest.A", x0.getTag());
316        assertEquals("DropBoxTest.B", x1.getTag());
317        assertEquals("DropBoxTest.A", x2.getTag());
318        assertEquals("A0", x0.getText(80));
319        assertEquals("B0", x1.getText(80));
320        assertEquals("A1", x2.getText(80));
321
322        a0.close();
323        a1.close();
324        b0.close();
325        x0.close();
326        x1.close();
327        x2.close();
328    }
329
330    public void testSizeLimits() throws Exception {
331        File dir = getEmptyDir("testSizeLimits");
332        int blockSize =  new StatFs(dir.getPath()).getBlockSize();
333
334        // Limit storage to 10 blocks
335        int kb = blockSize * 10 / 1024;
336        ContentResolver cr = getContext().getContentResolver();
337        Settings.Global.putString(cr, Settings.Global.DROPBOX_QUOTA_KB, Integer.toString(kb));
338
339        // Three tags using a total of 12 blocks:
340        // DropBoxTest0 [ ][ ]
341        // DropBoxTest1 [x][ ][    ][ ][xxx(20 blocks)xxx]
342        // DropBoxTest2 [xxxxxxxxxx][ ][ ]
343        //
344        // The blocks marked "x" will be removed due to storage restrictions.
345        // Use random fill (so it doesn't compress), subtract a little for gzip overhead
346
347        final int overhead = 64;
348        long before = System.currentTimeMillis();
349        DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
350        DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
351
352        addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead);
353        addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead);
354
355        addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead);
356        addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead);
357        addRandomEntry(dropbox, "DropBoxTest1", blockSize * 2 - overhead);
358        addRandomEntry(dropbox, "DropBoxTest1", blockSize - overhead);
359        addRandomEntry(dropbox, "DropBoxTest1", blockSize * 20 - overhead);
360
361        addRandomEntry(dropbox, "DropBoxTest2", blockSize * 4 - overhead);
362        addRandomEntry(dropbox, "DropBoxTest2", blockSize - overhead);
363        addRandomEntry(dropbox, "DropBoxTest2", blockSize - overhead);
364
365        DropBoxManager.Entry e0 = dropbox.getNextEntry(null, before);
366        DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
367        DropBoxManager.Entry e2 = dropbox.getNextEntry(null, e1.getTimeMillis());
368        DropBoxManager.Entry e3 = dropbox.getNextEntry(null, e2.getTimeMillis());
369        DropBoxManager.Entry e4 = dropbox.getNextEntry(null, e3.getTimeMillis());
370        DropBoxManager.Entry e5 = dropbox.getNextEntry(null, e4.getTimeMillis());
371        DropBoxManager.Entry e6 = dropbox.getNextEntry(null, e5.getTimeMillis());
372        DropBoxManager.Entry e7 = dropbox.getNextEntry(null, e6.getTimeMillis());
373        DropBoxManager.Entry e8 = dropbox.getNextEntry(null, e7.getTimeMillis());
374        DropBoxManager.Entry e9 = dropbox.getNextEntry(null, e8.getTimeMillis());
375        assertTrue(null == dropbox.getNextEntry(null, e9.getTimeMillis()));
376
377        assertEquals("DropBoxTest0", e0.getTag());
378        assertEquals("DropBoxTest0", e1.getTag());
379        assertEquals(blockSize - overhead, getEntrySize(e0));
380        assertEquals(blockSize - overhead, getEntrySize(e1));
381
382        assertEquals("DropBoxTest1", e2.getTag());
383        assertEquals("DropBoxTest1", e3.getTag());
384        assertEquals("DropBoxTest1", e4.getTag());
385        assertEquals("DropBoxTest1", e5.getTag());
386        assertEquals("DropBoxTest1", e6.getTag());
387        assertEquals(-1, getEntrySize(e2));  // Tombstone
388        assertEquals(blockSize - overhead, getEntrySize(e3));
389        assertEquals(blockSize * 2 - overhead, getEntrySize(e4));
390        assertEquals(blockSize - overhead, getEntrySize(e5));
391        assertEquals(-1, getEntrySize(e6));
392
393        assertEquals("DropBoxTest2", e7.getTag());
394        assertEquals("DropBoxTest2", e8.getTag());
395        assertEquals("DropBoxTest2", e9.getTag());
396        assertEquals(-1, getEntrySize(e7));  // Tombstone
397        assertEquals(blockSize - overhead, getEntrySize(e8));
398        assertEquals(blockSize - overhead, getEntrySize(e9));
399
400        e0.close();
401        e1.close();
402        e2.close();
403        e3.close();
404        e4.close();
405        e5.close();
406        e6.close();
407        e7.close();
408        e8.close();
409        e9.close();
410
411        // Specifying a tag name skips tombstone records.
412
413        DropBoxManager.Entry t0 = dropbox.getNextEntry("DropBoxTest1", before);
414        DropBoxManager.Entry t1 = dropbox.getNextEntry("DropBoxTest1", t0.getTimeMillis());
415        DropBoxManager.Entry t2 = dropbox.getNextEntry("DropBoxTest1", t1.getTimeMillis());
416        assertTrue(null == dropbox.getNextEntry("DropBoxTest1", t2.getTimeMillis()));
417
418        assertEquals("DropBoxTest1", t0.getTag());
419        assertEquals("DropBoxTest1", t1.getTag());
420        assertEquals("DropBoxTest1", t2.getTag());
421
422        assertEquals(blockSize - overhead, getEntrySize(t0));
423        assertEquals(blockSize * 2 - overhead, getEntrySize(t1));
424        assertEquals(blockSize - overhead, getEntrySize(t2));
425
426        t0.close();
427        t1.close();
428        t2.close();
429    }
430
431    public void testAgeLimits() throws Exception {
432        File dir = getEmptyDir("testAgeLimits");
433        int blockSize = new StatFs(dir.getPath()).getBlockSize();
434
435        // Limit storage to 10 blocks with an expiration of 1 second
436        int kb = blockSize * 10 / 1024;
437        ContentResolver cr = getContext().getContentResolver();
438        Settings.Global.putString(cr, Settings.Global.DROPBOX_AGE_SECONDS, "1");
439        Settings.Global.putString(cr, Settings.Global.DROPBOX_QUOTA_KB, Integer.toString(kb));
440
441        // Write one normal entry and another so big that it is instantly tombstoned
442        long before = System.currentTimeMillis();
443        DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
444        DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
445
446        dropbox.addText("DropBoxTest", "TEST");
447        addRandomEntry(dropbox, "DropBoxTest", blockSize * 20);
448
449        // Verify that things are as expected
450        DropBoxManager.Entry e0 = dropbox.getNextEntry(null, before);
451        DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
452        assertTrue(null == dropbox.getNextEntry(null, e1.getTimeMillis()));
453
454        assertEquals("TEST", e0.getText(80));
455        assertEquals(null, e1.getText(80));
456        assertEquals(-1, getEntrySize(e1));
457
458        e0.close();
459        e1.close();
460
461        // Wait a second and write another entry -- old ones should be expunged
462        Thread.sleep(2000);
463        dropbox.addText("DropBoxTest", "TEST1");
464
465        e0 = dropbox.getNextEntry(null, before);
466        assertTrue(null == dropbox.getNextEntry(null, e0.getTimeMillis()));
467        assertEquals("TEST1", e0.getText(80));
468        e0.close();
469    }
470
471    public void testFileCountLimits() throws Exception {
472        File dir = getEmptyDir("testFileCountLimits");
473
474        DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
475        DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
476        dropbox.addText("DropBoxTest", "TEST0");
477        dropbox.addText("DropBoxTest", "TEST1");
478        dropbox.addText("DropBoxTest", "TEST2");
479        dropbox.addText("DropBoxTest", "TEST3");
480        dropbox.addText("DropBoxTest", "TEST4");
481        dropbox.addText("DropBoxTest", "TEST5");
482
483        // Verify 6 files added
484        DropBoxManager.Entry e0 = dropbox.getNextEntry(null, 0);
485        DropBoxManager.Entry e1 = dropbox.getNextEntry(null, e0.getTimeMillis());
486        DropBoxManager.Entry e2 = dropbox.getNextEntry(null, e1.getTimeMillis());
487        DropBoxManager.Entry e3 = dropbox.getNextEntry(null, e2.getTimeMillis());
488        DropBoxManager.Entry e4 = dropbox.getNextEntry(null, e3.getTimeMillis());
489        DropBoxManager.Entry e5 = dropbox.getNextEntry(null, e4.getTimeMillis());
490        assertTrue(null == dropbox.getNextEntry(null, e5.getTimeMillis()));
491        assertEquals("TEST0", e0.getText(80));
492        assertEquals("TEST5", e5.getText(80));
493
494        e0.close();
495        e1.close();
496        e2.close();
497        e3.close();
498        e4.close();
499        e5.close();
500
501        // Limit to 3 files and add one more entry
502        ContentResolver cr = getContext().getContentResolver();
503        Settings.Global.putString(cr, Settings.Global.DROPBOX_MAX_FILES, "3");
504        dropbox.addText("DropBoxTest", "TEST6");
505
506        // Verify only 3 files left
507        DropBoxManager.Entry f0 = dropbox.getNextEntry(null, 0);
508        DropBoxManager.Entry f1 = dropbox.getNextEntry(null, f0.getTimeMillis());
509        DropBoxManager.Entry f2 = dropbox.getNextEntry(null, f1.getTimeMillis());
510        assertTrue(null == dropbox.getNextEntry(null, f2.getTimeMillis()));
511        assertEquals("TEST4", f0.getText(80));
512        assertEquals("TEST5", f1.getText(80));
513        assertEquals("TEST6", f2.getText(80));
514
515        f0.close();
516        f1.close();
517        f2.close();
518    }
519
520    public void testCreateDropBoxManagerWithInvalidDirectory() throws Exception {
521        // If created with an invalid directory, the DropBoxManager should suffer quietly
522        // and fail all operations (this is how it survives a full disk).
523        // Once the directory becomes possible to create, it will start working.
524
525        File dir = new File(getEmptyDir("testCreateDropBoxManagerWith"), "InvalidDirectory");
526        new FileOutputStream(dir).close();  // Create an empty file
527        DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
528        DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
529
530        dropbox.addText("DropBoxTest", "should be ignored");
531        dropbox.addData("DropBoxTest", "should be ignored".getBytes(), 0);
532        assertTrue(null == dropbox.getNextEntry("DropBoxTest", 0));
533
534        dir.delete();  // Remove the file so a directory can be created
535        dropbox.addText("DropBoxTest", "TEST");
536        DropBoxManager.Entry e = dropbox.getNextEntry("DropBoxTest", 0);
537        assertTrue(null == dropbox.getNextEntry("DropBoxTest", e.getTimeMillis()));
538        assertEquals("DropBoxTest", e.getTag());
539        assertEquals("TEST", e.getText(80));
540        e.close();
541    }
542
543    public void testDropBoxEntrySerialization() throws Exception {
544        // Make sure DropBoxManager.Entry can be serialized to a Parcel and back
545        // under a variety of conditions.
546
547        Parcel parcel = Parcel.obtain();
548        File dir = getEmptyDir("testDropBoxEntrySerialization");
549
550        new DropBoxManager.Entry("empty", 1000000).writeToParcel(parcel, 0);
551        new DropBoxManager.Entry("string", 2000000, "String Value").writeToParcel(parcel, 0);
552        new DropBoxManager.Entry("bytes", 3000000, "Bytes Value".getBytes(),
553                DropBoxManager.IS_TEXT).writeToParcel(parcel, 0);
554        new DropBoxManager.Entry("zerobytes", 4000000, new byte[0], 0).writeToParcel(parcel, 0);
555        new DropBoxManager.Entry("emptybytes", 5000000, (byte[]) null,
556                DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
557
558        try {
559            new DropBoxManager.Entry("badbytes", 99999,
560                    "Bad Bytes Value".getBytes(),
561                    DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
562            fail("IllegalArgumentException expected for non-null byte[] and IS_EMPTY flags");
563        } catch (IllegalArgumentException e) {
564            // expected
565        }
566
567        try {
568            new DropBoxManager.Entry("badbytes", 99999, (byte[]) null, 0).writeToParcel(parcel, 0);
569            fail("IllegalArgumentException expected for null byte[] and non-IS_EMPTY flags");
570        } catch (IllegalArgumentException e) {
571            // expected
572        }
573
574        File f = new File(dir, "file.dat");
575        FileOutputStream os = new FileOutputStream(f);
576        os.write("File Value".getBytes());
577        os.close();
578
579        new DropBoxManager.Entry("file", 6000000, f, DropBoxManager.IS_TEXT).writeToParcel(
580                parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
581        new DropBoxManager.Entry("binfile", 7000000, f, 0).writeToParcel(
582                parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
583        new DropBoxManager.Entry("emptyfile", 8000000, (ParcelFileDescriptor) null,
584                DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
585
586        try {
587            new DropBoxManager.Entry("badfile", 99999, new File(dir, "nonexist.dat"), 0);
588            fail("IOException expected for nonexistent file");
589        } catch (IOException e) {
590            // expected
591        }
592
593        try {
594            new DropBoxManager.Entry("badfile", 99999, f, DropBoxManager.IS_EMPTY).writeToParcel(
595                    parcel, 0);
596            fail("IllegalArgumentException expected for non-null file and IS_EMPTY flags");
597        } catch (IllegalArgumentException e) {
598            // expected
599        }
600
601        try {
602            new DropBoxManager.Entry("badfile", 99999, (ParcelFileDescriptor) null, 0);
603            fail("IllegalArgumentException expected for null PFD and non-IS_EMPTY flags");
604        } catch (IllegalArgumentException e) {
605            // expected
606        }
607
608        File gz = new File(dir, "file.gz");
609        GZIPOutputStream gzout = new GZIPOutputStream(new FileOutputStream(gz));
610        gzout.write("Gzip File Value".getBytes());
611        gzout.close();
612
613        new DropBoxManager.Entry("gzipfile", 9000000, gz,
614                DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED).writeToParcel(
615                    parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
616        new DropBoxManager.Entry("gzipbinfile", 10000000, gz,
617                DropBoxManager.IS_GZIPPED).writeToParcel(
618                    parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
619
620        //
621        // Switch from writing to reading
622        //
623
624        parcel.setDataPosition(0);
625        DropBoxManager.Entry e;
626
627        e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
628        assertEquals("empty", e.getTag());
629        assertEquals(1000000, e.getTimeMillis());
630        assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
631        assertEquals(null, e.getText(100));
632        assertEquals(null, e.getInputStream());
633        e.close();
634
635        e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
636        assertEquals("string", e.getTag());
637        assertEquals(2000000, e.getTimeMillis());
638        assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
639        assertEquals("String Value", e.getText(100));
640        assertEquals("String Value",
641                new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
642        e.close();
643
644        e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
645        assertEquals("bytes", e.getTag());
646        assertEquals(3000000, e.getTimeMillis());
647        assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
648        assertEquals("Bytes Value", e.getText(100));
649        e.close();
650
651        e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
652        assertEquals("zerobytes", e.getTag());
653        assertEquals(4000000, e.getTimeMillis());
654        assertEquals(0, e.getFlags());
655        assertEquals(null, e.getText(100));
656        assertEquals(null,
657                new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
658        e.close();
659
660        e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
661        assertEquals("emptybytes", e.getTag());
662        assertEquals(5000000, e.getTimeMillis());
663        assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
664        assertEquals(null, e.getText(100));
665        assertEquals(null, e.getInputStream());
666        e.close();
667
668        e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
669        assertEquals("file", e.getTag());
670        assertEquals(6000000, e.getTimeMillis());
671        assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
672        assertEquals("File Value", e.getText(100));
673        e.close();
674
675        e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
676        assertEquals("binfile", e.getTag());
677        assertEquals(7000000, e.getTimeMillis());
678        assertEquals(0, e.getFlags());
679        assertEquals(null, e.getText(100));
680        assertEquals("File Value",
681                new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
682        e.close();
683        e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
684        assertEquals("emptyfile", e.getTag());
685        assertEquals(8000000, e.getTimeMillis());
686        assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
687        assertEquals(null, e.getText(100));
688        assertEquals(null, e.getInputStream());
689        e.close();
690
691        e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
692        assertEquals("gzipfile", e.getTag());
693        assertEquals(9000000, e.getTimeMillis());
694        assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
695        assertEquals("Gzip File Value", e.getText(100));
696        e.close();
697
698        e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
699        assertEquals("gzipbinfile", e.getTag());
700        assertEquals(10000000, e.getTimeMillis());
701        assertEquals(0, e.getFlags());
702        assertEquals(null, e.getText(100));
703        assertEquals("Gzip File Value",
704                new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
705        e.close();
706        assertEquals(0, parcel.dataAvail());
707        parcel.recycle();
708    }
709
710    public void testDropBoxEntrySerializationDoesntLeakFileDescriptors() throws Exception {
711        File dir = getEmptyDir("testDropBoxEntrySerialization");
712        File f = new File(dir, "file.dat");
713        FileOutputStream os = new FileOutputStream(f);
714        os.write("File Value".getBytes());
715        os.close();
716
717        int before = countOpenFiles();
718        assertTrue(before > 0);
719
720        for (int i = 0; i < 1000; i++) {
721            Parcel parcel = Parcel.obtain();
722            new DropBoxManager.Entry("file", 1000000, f, 0).writeToParcel(
723                    parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
724
725            parcel.setDataPosition(0);
726            DropBoxManager.Entry e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
727            assertEquals("file", e.getTag());
728            e.close();
729
730            parcel.recycle();
731        }
732
733        int after = countOpenFiles();
734        assertTrue(after > 0);
735        assertTrue(after < before + 20);
736    }
737
738    private void addRandomEntry(DropBoxManager dropbox, String tag, int size) throws Exception {
739        byte[] bytes = new byte[size];
740        new Random(System.currentTimeMillis()).nextBytes(bytes);
741
742        File f = new File(getEmptyDir("addRandomEntry"), "random.dat");
743        FileOutputStream os = new FileOutputStream(f);
744        os.write(bytes);
745        os.close();
746
747        dropbox.addFile(tag, f, 0);
748    }
749
750    private int getEntrySize(DropBoxManager.Entry e) throws Exception {
751        InputStream is = e.getInputStream();
752        if (is == null) return -1;
753        int length = 0;
754        while (is.read() != -1) length++;
755        return length;
756    }
757
758    private void recursiveDelete(File file) {
759        if (!file.delete() && file.isDirectory()) {
760            for (File f : file.listFiles()) recursiveDelete(f);
761            file.delete();
762        }
763    }
764
765    private File getEmptyDir(String name) {
766        File dir = getContext().getDir("DropBoxTest." + name, 0);
767        for (File f : dir.listFiles()) recursiveDelete(f);
768        assertTrue(dir.listFiles().length == 0);
769        return dir;
770    }
771
772    private int countOpenFiles() {
773        return new File("/proc/" + Process.myPid() + "/fd").listFiles().length;
774    }
775}
776