OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / tests / coretests / src / android / os / storage / StorageManagerBaseTest.java
1 /*
2  * Copyright (C) 2010 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 android.os.storage;
18
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.content.res.Resources.NotFoundException;
22 import android.os.Environment;
23 import android.os.SystemClock;
24 import android.test.InstrumentationTestCase;
25 import android.util.Log;
26 import android.os.Environment;
27 import android.os.FileUtils;
28 import android.os.storage.OnObbStateChangeListener;
29 import android.os.storage.StorageManager;
30
31 import java.io.BufferedReader;
32 import java.io.DataInputStream;
33 import java.io.File;
34 import java.io.FileInputStream;
35 import java.io.FileNotFoundException;
36 import java.io.FileReader;
37 import java.io.InputStream;
38 import java.io.IOException;
39 import java.io.StringReader;
40
41 public class StorageManagerBaseTest extends InstrumentationTestCase {
42
43     protected Context mContext = null;
44     protected StorageManager mSm = null;
45     private static String LOG_TAG = "StorageManagerBaseTest";
46     protected static final long MAX_WAIT_TIME = 120*1000;
47     protected static final long WAIT_TIME_INCR = 5*1000;
48     protected static String OBB_FILE_1 = "obb_file1.obb";
49     protected static String OBB_FILE_1_CONTENTS_1 = "OneToOneThousandInts.bin";
50     protected static String OBB_FILE_2 = "obb_file2.obb";
51     protected static String OBB_FILE_3 = "obb_file3.obb";
52     protected static String OBB_FILE_1_PASSWORD = "password1";
53     protected static String OBB_FILE_1_ENCRYPTED = "obb_enc_file100_orig1.obb";
54     protected static String OBB_FILE_2_UNSIGNED = "obb_file2_nosign.obb";
55     protected static String OBB_FILE_3_PASSWORD = "password3";
56     protected static String OBB_FILE_3_ENCRYPTED = "obb_enc_file100_orig3.obb";
57     protected static String OBB_FILE_3_BAD_PACKAGENAME = "obb_file3_bad_packagename.obb";
58
59     protected static boolean FORCE = true;
60     protected static boolean DONT_FORCE = false;
61
62     private static final String SAMPLE1_TEXT = "This is sample text.\n\nTesting 1 2 3.";
63
64     private static final String SAMPLE2_TEXT =
65         "We the people of the United States, in order to form a more perfect union,\n"
66         + "establish justice, insure domestic tranquility, provide for the common\n"
67         + "defense, promote the general welfare, and secure the blessings of liberty\n"
68         + "to ourselves and our posterity, do ordain and establish this Constitution\n"
69         + "for the United States of America.\n\n";
70
71     class MountingObbThread extends Thread {
72         boolean mStop = false;
73         volatile boolean mFileOpenOnObb = false;
74         private String mObbFilePath = null;
75         private String mPathToContentsFile = null;
76         private String mOfficialObbFilePath = null;
77
78         /**
79          * Constructor
80          *
81          * @param obbFilePath path to the OBB image file
82          * @param pathToContentsFile path to a file on the mounted OBB volume to open after the OBB
83          *      has been mounted
84          */
85         public MountingObbThread (String obbFilePath, String pathToContentsFile) {
86             assertTrue("obbFilePath cannot be null!", obbFilePath != null);
87             mObbFilePath = obbFilePath;
88             assertTrue("path to contents file cannot be null!", pathToContentsFile != null);
89             mPathToContentsFile = pathToContentsFile;
90         }
91
92         /**
93          * Runs the thread
94          *
95          * Mounts OBB_FILE_1, and tries to open a file on the mounted OBB (specified in the
96          * constructor). Once it's open, it waits until someone calls its doStop(), after which it
97          * closes the opened file.
98          */
99         public void run() {
100             // the official OBB file path and the mount-request file path should be the same, but
101             // let's distinguish the two as they may make for some interesting tests later
102             mOfficialObbFilePath = mountObb(mObbFilePath);
103             assertEquals("Expected and actual OBB file paths differ!", mObbFilePath,
104                     mOfficialObbFilePath);
105
106             // open a file on OBB 1...
107             DataInputStream inputFile = openFileOnMountedObb(mOfficialObbFilePath,
108                     mPathToContentsFile);
109             assertTrue("Failed to open file!", inputFile != null);
110
111             synchronized (this) {
112                 mFileOpenOnObb = true;
113                 notifyAll();
114             }
115
116             while (!mStop) {
117                 try {
118                     Thread.sleep(WAIT_TIME_INCR);
119                 } catch (InterruptedException e) {
120                     // nothing special to be done for interruptions
121                 }
122             }
123             try {
124                 inputFile.close();
125             } catch (IOException e) {
126                 fail("Failed to close file on OBB due to error: " + e.toString());
127             }
128         }
129
130         /**
131          * Tells whether a file has yet been successfully opened on the OBB or not
132          *
133          * @return true if the specified file on the OBB was opened; false otherwise
134          */
135         public boolean isFileOpenOnObb() {
136             return mFileOpenOnObb;
137         }
138
139         /**
140          * Returns the official path of the OBB file that was mounted
141          *
142          * This is not the mount path, but the normalized path to the actual OBB file
143          *
144          * @return a {@link String} representation of the path to the OBB file that was mounted
145          */
146         public String officialObbFilePath() {
147             return mOfficialObbFilePath;
148         }
149
150         /**
151          * Requests the thread to stop running
152          *
153          * Closes the opened file and returns
154          */
155         public void doStop() {
156             mStop = true;
157         }
158     }
159
160     public class ObbListener extends OnObbStateChangeListener {
161         private String LOG_TAG = "StorageManagerBaseTest.ObbListener";
162
163         String mOfficialPath = null;
164         boolean mDone = false;
165         int mState = -1;
166
167         /**
168          * {@inheritDoc}
169          */
170         @Override
171         public void onObbStateChange(String path, int state) {
172             Log.i(LOG_TAG, "Storage state changing to: " + state);
173
174             synchronized (this) {
175                 Log.i(LOG_TAG, "OfficialPath is now: " + path);
176                 mState = state;
177                 mOfficialPath = path;
178                 mDone = true;
179                 notifyAll();
180             }
181         }
182
183         /**
184          * Tells whether we are done or not (system told us the OBB has changed state)
185          *
186          * @return true if the system has told us this OBB's state has changed, false otherwise
187          */
188         public boolean isDone() {
189             return mDone;
190         }
191
192         /**
193          * The last state of the OBB, according to the system
194          *
195          * @return A {@link String} representation of the state of the OBB
196          */
197         public int state() {
198             return mState;
199         }
200
201         /**
202          * The normalized, official path to the OBB file (according to the system)
203          *
204          * @return A {@link String} representation of the official path to the OBB file
205          */
206         public String officialPath() {
207             return mOfficialPath;
208         }
209     }
210
211     /**
212      * {@inheritDoc}
213      */
214     @Override
215     public void setUp() throws Exception {
216         mContext = getInstrumentation().getContext();
217         mSm = (StorageManager)mContext.getSystemService(android.content.Context.STORAGE_SERVICE);
218
219     }
220
221     /**
222      * Helper to copy a raw resource file to an actual specified file
223      *
224      * @param rawResId The raw resource ID of the OBB resource file
225      * @param outFile A File representing the file we want to copy the OBB to
226      * @throws NotFoundException If the resource file could not be found
227      */
228     private void copyRawToFile(int rawResId, File outFile) throws NotFoundException {
229         Resources res = mContext.getResources();
230         InputStream is = null;
231         try {
232             is = res.openRawResource(rawResId);
233         } catch (NotFoundException e) {
234             Log.i(LOG_TAG, "Failed to load resource with id: " + rawResId);
235             throw e;
236         }
237         FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
238                 | FileUtils.S_IRWXO, -1, -1);
239         assertTrue(FileUtils.copyToFile(is, outFile));
240         FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
241                 | FileUtils.S_IRWXO, -1, -1);
242     }
243
244     /**
245      * Creates an OBB file (with the given name), into the app's standard files directory
246      *
247      * @param name The name of the OBB file we want to create/write to
248      * @param rawResId The raw resource ID of the OBB file in the package
249      * @return A {@link File} representing the file to write to
250      */
251     protected File createObbFile(String name, int rawResId) {
252         File outFile = null;
253         try {
254             final File filesDir = mContext.getFilesDir();
255             outFile = new File(filesDir, name);
256             copyRawToFile(rawResId, outFile);
257         } catch (NotFoundException e) {
258             if (outFile != null) {
259                 outFile.delete();
260             }
261         }
262         return outFile;
263     }
264
265     /**
266      * Mounts an OBB file and opens a file located on it
267      *
268      * @param obbPath Path to OBB image
269      * @param fileName The full name and path to the file on the OBB to open once the OBB is mounted
270      * @return The {@link DataInputStream} representing the opened file, if successful in opening
271      *      the file, or null of unsuccessful.
272      */
273     protected DataInputStream openFileOnMountedObb(String obbPath, String fileName) {
274
275         // get mSm obb mount path
276         assertTrue("Cannot open file when OBB is not mounted!", mSm.isObbMounted(obbPath));
277
278         String path = mSm.getMountedObbPath(obbPath);
279         assertTrue("Path should not be null!", path != null);
280
281         File inFile = new File(path, fileName);
282         DataInputStream inStream = null;
283         try {
284             inStream = new DataInputStream(new FileInputStream(inFile));
285             Log.i(LOG_TAG, "Opened file: " + fileName + " for read at path: " + path);
286         } catch (FileNotFoundException e) {
287             Log.e(LOG_TAG, e.toString());
288             return null;
289         } catch (SecurityException e) {
290             Log.e(LOG_TAG, e.toString());
291             return null;
292         }
293         return inStream;
294     }
295
296     /**
297      * Mounts an OBB file
298      *
299      * @param obbFilePath The full path to the OBB file to mount
300      * @param key (optional) The key to use to unencrypt the OBB; pass null for no encryption
301      * @param expectedState The expected state resulting from trying to mount the OBB
302      * @return A {@link String} representing the normalized path to OBB file that was mounted
303      */
304     protected String mountObb(String obbFilePath, String key, int expectedState) {
305         return doMountObb(obbFilePath, key, expectedState);
306     }
307
308     /**
309      * Mounts an OBB file with default options (no encryption, mounting succeeds)
310      *
311      * @param obbFilePath The full path to the OBB file to mount
312      * @return A {@link String} representing the normalized path to OBB file that was mounted
313      */
314     protected String mountObb(String obbFilePath) {
315         return doMountObb(obbFilePath, null, OnObbStateChangeListener.MOUNTED);
316     }
317
318     /**
319      * Synchronously waits for an OBB listener to be signaled of a state change, but does not throw
320      *
321      * @param obbListener The listener for the OBB file
322      * @return true if the listener was signaled of a state change by the system, else returns
323      *      false if we time out.
324      */
325     protected boolean doWaitForObbStateChange(ObbListener obbListener) {
326         synchronized(obbListener) {
327             long waitTimeMillis = 0;
328             while (!obbListener.isDone()) {
329                 try {
330                     Log.i(LOG_TAG, "Waiting for listener...");
331                     obbListener.wait(WAIT_TIME_INCR);
332                     Log.i(LOG_TAG, "Awoke from waiting for listener...");
333                     waitTimeMillis += WAIT_TIME_INCR;
334                     if (waitTimeMillis > MAX_WAIT_TIME) {
335                         fail("Timed out waiting for OBB state to change!");
336                     }
337                 } catch (InterruptedException e) {
338                     Log.i(LOG_TAG, e.toString());
339                 }
340             }
341             return obbListener.isDone();
342             }
343     }
344
345     /**
346      * Synchronously waits for an OBB listener to be signaled of a state change
347      *
348      * @param obbListener The listener for the OBB file
349      * @return true if the listener was signaled of a state change by the system; else a fail()
350      *      is triggered if we timed out
351      */
352     protected String doMountObb_noThrow(String obbFilePath, String key, int expectedState) {
353         Log.i(LOG_TAG, "doMountObb() on " + obbFilePath + " using key: " + key);
354         assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
355         assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
356
357         ObbListener obbListener = new ObbListener();
358         boolean success = mSm.mountObb(obbFilePath, key, obbListener);
359         success &= obbFilePath.equals(doWaitForObbStateChange(obbListener));
360         success &= (expectedState == obbListener.state());
361
362         if (OnObbStateChangeListener.MOUNTED == expectedState) {
363             success &= obbFilePath.equals(obbListener.officialPath());
364             success &= mSm.isObbMounted(obbListener.officialPath());
365         } else {
366             success &= !mSm.isObbMounted(obbListener.officialPath());
367         }
368
369         if (success) {
370             return obbListener.officialPath();
371         } else {
372             return null;
373         }
374     }
375
376     /**
377      * Mounts an OBB file without throwing and synchronously waits for it to finish mounting
378      *
379      * @param obbFilePath The full path to the OBB file to mount
380      * @param key (optional) The key to use to unencrypt the OBB; pass null for no encryption
381      * @param expectedState The expected state resulting from trying to mount the OBB
382      * @return A {@link String} representing the actual normalized path to OBB file that was
383      *      mounted, or null if the mounting failed
384      */
385     protected String doMountObb(String obbFilePath, String key, int expectedState) {
386         Log.i(LOG_TAG, "doMountObb() on " + obbFilePath + " using key: " + key);
387         assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
388
389         ObbListener obbListener = new ObbListener();
390         assertTrue("mountObb call failed", mSm.mountObb(obbFilePath, key, obbListener));
391         assertTrue("Failed to get OBB mount status change for file: " + obbFilePath,
392                 doWaitForObbStateChange(obbListener));
393         assertEquals("OBB mount state not what was expected!", expectedState, obbListener.state());
394
395         if (OnObbStateChangeListener.MOUNTED == expectedState) {
396             assertEquals(obbFilePath, obbListener.officialPath());
397             assertTrue("Obb should be mounted, but SM reports it is not!",
398                     mSm.isObbMounted(obbListener.officialPath()));
399         } else if (OnObbStateChangeListener.UNMOUNTED == expectedState) {
400             assertFalse("Obb should not be mounted, but SM reports it is!",
401                     mSm.isObbMounted(obbListener.officialPath()));
402         }
403
404         assertEquals("Mount state is not what was expected!", expectedState, obbListener.state());
405         return obbListener.officialPath();
406     }
407
408     /**
409      * Unmounts an OBB file without throwing, and synchronously waits for it to finish unmounting
410      *
411      * @param obbFilePath The full path to the OBB file to mount
412      * @param force true if we shuold force the unmount, false otherwise
413      * @return true if the unmount was successful, false otherwise
414      */
415     protected boolean unmountObb_noThrow(String obbFilePath, boolean force) {
416         Log.i(LOG_TAG, "doUnmountObb_noThrow() on " + obbFilePath);
417         assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
418         boolean success = true;
419
420         ObbListener obbListener = new ObbListener();
421         assertTrue("unmountObb call failed", mSm.unmountObb(obbFilePath, force, obbListener));
422
423         boolean stateChanged = doWaitForObbStateChange(obbListener);
424         if (force) {
425             success &= stateChanged;
426             success &= (OnObbStateChangeListener.UNMOUNTED == obbListener.state());
427             success &= !mSm.isObbMounted(obbFilePath);
428         }
429         return success;
430     }
431
432     /**
433      * Unmounts an OBB file and synchronously waits for it to finish unmounting
434      *
435      * @param obbFilePath The full path to the OBB file to mount
436      * @param force true if we shuold force the unmount, false otherwise
437      */
438     protected void unmountObb(String obbFilePath, boolean force) {
439         Log.i(LOG_TAG, "doUnmountObb() on " + obbFilePath);
440         assertTrue ("Null path was passed in for OBB file!", obbFilePath != null);
441
442         ObbListener obbListener = new ObbListener();
443         assertTrue("unmountObb call failed", mSm.unmountObb(obbFilePath, force, obbListener));
444
445         boolean stateChanged = doWaitForObbStateChange(obbListener);
446         if (force) {
447             assertTrue("Timed out waiting to unmount OBB file " + obbFilePath, stateChanged);
448             assertEquals("OBB failed to unmount", OnObbStateChangeListener.UNMOUNTED,
449                     obbListener.state());
450             assertFalse("Obb should NOT be mounted, but SM reports it is!", mSm.isObbMounted(
451                     obbFilePath));
452         }
453     }
454
455     /**
456      * Helper to validate the contents of an "int" file in an OBB.
457      *
458      * The format of the files are sequential int's, in the range of: [start..end)
459      *
460      * @param path The full path to the file (path to OBB)
461      * @param filename The filename containing the ints to validate
462      * @param start The first int expected to be found in the file
463      * @param end The last int + 1 expected to be found in the file
464      */
465     protected void doValidateIntContents(String path, String filename, int start, int end) {
466         File inFile = new File(path, filename);
467         DataInputStream inStream = null;
468         Log.i(LOG_TAG, "Validating file " + filename + " at " + path);
469         try {
470             inStream = new DataInputStream(new FileInputStream(inFile));
471
472             for (int i = start; i < end; ++i) {
473                 if (inStream.readInt() != i) {
474                     fail("Unexpected value read in OBB file");
475                 }
476             }
477             if (inStream != null) {
478                 inStream.close();
479             }
480             Log.i(LOG_TAG, "Successfully validated file " + filename);
481         } catch (FileNotFoundException e) {
482             fail("File " + inFile + " not found: " + e.toString());
483         } catch (IOException e) {
484             fail("IOError with file " + inFile + ":" + e.toString());
485         }
486     }
487
488     /**
489      * Helper to validate the contents of a text file in an OBB
490      *
491      * @param path The full path to the file (path to OBB)
492      * @param filename The filename containing the ints to validate
493      * @param contents A {@link String} containing the expected contents of the file
494      */
495     protected void doValidateTextContents(String path, String filename, String contents) {
496         File inFile = new File(path, filename);
497         BufferedReader fileReader = null;
498         BufferedReader textReader = null;
499         Log.i(LOG_TAG, "Validating file " + filename + " at " + path);
500         try {
501             fileReader = new BufferedReader(new FileReader(inFile));
502             textReader = new BufferedReader(new StringReader(contents));
503             String actual = null;
504             String expected = null;
505             while ((actual = fileReader.readLine()) != null) {
506                 expected = textReader.readLine();
507                 if (!actual.equals(expected)) {
508                     fail("File " + filename + " in OBB " + path + " does not match expected value");
509                 }
510             }
511             fileReader.close();
512             textReader.close();
513             Log.i(LOG_TAG, "File " + filename + " successfully verified.");
514         } catch (IOException e) {
515             fail("IOError with file " + inFile + ":" + e.toString());
516         }
517     }
518
519     /**
520      * Helper to validate the contents of a "long" file on our OBBs
521      *
522      * The format of the files are sequential 0's of type long
523      *
524      * @param path The full path to the file (path to OBB)
525      * @param filename The filename containing the ints to validate
526      * @param size The number of zero's expected in the file
527      * @param checkContents If true, the contents of the file are actually verified; if false,
528      *      we simply verify that the file can be opened
529      */
530     protected void doValidateZeroLongFile(String path, String filename, long size,
531             boolean checkContents) {
532         File inFile = new File(path, filename);
533         DataInputStream inStream = null;
534         Log.i(LOG_TAG, "Validating file " + filename + " at " + path);
535         try {
536             inStream = new DataInputStream(new FileInputStream(inFile));
537
538             if (checkContents) {
539                 for (long i = 0; i < size; ++i) {
540                     if (inStream.readLong() != 0) {
541                         fail("Unexpected value read in OBB file" + filename);
542                     }
543                 }
544             }
545
546             if (inStream != null) {
547                 inStream.close();
548             }
549             Log.i(LOG_TAG, "File " + filename + " successfully verified for " + size + " zeros");
550         } catch (IOException e) {
551             fail("IOError with file " + inFile + ":" + e.toString());
552         }
553     }
554
555     /**
556      * Helper to synchronously wait until we can get a path for a given OBB file
557      *
558      * @param filePath The full normalized path to the OBB file
559      * @return The mounted path of the OBB, used to access contents in it
560      */
561     protected String doWaitForPath(String filePath) {
562         String path = null;
563
564         long waitTimeMillis = 0;
565         assertTrue("OBB " + filePath + " is not currently mounted!", mSm.isObbMounted(filePath));
566         while (path == null) {
567             try {
568                 Thread.sleep(WAIT_TIME_INCR);
569                 waitTimeMillis += WAIT_TIME_INCR;
570                 if (waitTimeMillis > MAX_WAIT_TIME) {
571                     fail("Timed out waiting to get path of OBB file " + filePath);
572                 }
573             } catch (InterruptedException e) {
574                 // do nothing
575             }
576             path = mSm.getMountedObbPath(filePath);
577         }
578         Log.i(LOG_TAG, "Got OBB path: " + path);
579         return path;
580     }
581
582     /**
583      * Verifies the pre-defined contents of our first OBB (OBB_FILE_1)
584      *
585      * The OBB contains 4 files and no subdirectories
586      *
587      * @param filePath The normalized path to the already-mounted OBB file
588      */
589     protected void verifyObb1Contents(String filePath) {
590         String path = null;
591         path = doWaitForPath(filePath);
592
593         // Validate contents of 2 files in this obb
594         doValidateIntContents(path, "OneToOneThousandInts.bin", 0, 1000);
595         doValidateIntContents(path, "SevenHundredInts.bin", 0, 700);
596         doValidateZeroLongFile(path, "FiveLongs.bin", 5, true);
597     }
598
599     /**
600      * Verifies the pre-defined contents of our second OBB (OBB_FILE_2)
601      *
602      * The OBB contains 2 files and no subdirectories
603      *
604      * @param filePath The normalized path to the already-mounted OBB file
605      */
606     protected void verifyObb2Contents(String filename) {
607         String path = null;
608         path = doWaitForPath(filename);
609
610         // Validate contents of file
611         doValidateTextContents(path, "sample.txt", SAMPLE1_TEXT);
612         doValidateTextContents(path, "sample2.txt", SAMPLE2_TEXT);
613     }
614
615     /**
616      * Verifies the pre-defined contents of our third OBB (OBB_FILE_3)
617      *
618      * The OBB contains nested files and subdirectories
619      *
620      * @param filePath The normalized path to the already-mounted OBB file
621      */
622     protected void verifyObb3Contents(String filename) {
623         String path = null;
624         path = doWaitForPath(filename);
625
626         // Validate contents of file
627         doValidateIntContents(path, "OneToOneThousandInts.bin", 0, 1000);
628         doValidateZeroLongFile(path, "TwoHundredLongs", 200, true);
629
630         // validate subdirectory 1
631         doValidateZeroLongFile(path + File.separator + "subdir1", "FiftyLongs", 50, true);
632
633         // validate subdirectory subdir2/
634         doValidateIntContents(path + File.separator + "subdir2", "OneToOneThousandInts", 0, 1000);
635
636         // validate subdirectory subdir2/subdir2a/
637         doValidateZeroLongFile(path + File.separator + "subdir2" + File.separator + "subdir2a",
638                 "TwoHundredLongs", 200, true);
639
640         // validate subdirectory subdir2/subdir2a/subdir2a1/
641         doValidateIntContents(path + File.separator + "subdir2" + File.separator + "subdir2a"
642                 + File.separator + "subdir2a1", "OneToOneThousandInts", 0, 1000);
643     }
644 }