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 com.android.server;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.content.res.Resources.NotFoundException;
22 import android.os.FileUtils;
23 import android.os.storage.OnObbStateChangeListener;
24 import android.os.storage.StorageManager;
25 import android.test.AndroidTestCase;
26 import android.test.ComparisonFailure;
27 import android.test.suitebuilder.annotation.LargeTest;
28 import android.util.Log;
29 
30 import com.android.frameworks.servicestests.R;
31 
32 import java.io.File;
33 import java.io.InputStream;
34 
35 public class MountServiceTests extends AndroidTestCase {
36     private static final String TAG = "MountServiceTests";
37 
38     private static final long MAX_WAIT_TIME = 25*1000;
39     private static final long WAIT_TIME_INCR = 5*1000;
40 
41     private static final String OBB_MOUNT_PREFIX = "/mnt/obb/";
42 
assertStartsWith(String message, String prefix, String actual)43     private static void assertStartsWith(String message, String prefix, String actual) {
44         if (!actual.startsWith(prefix)) {
45             throw new ComparisonFailure(message, prefix, actual);
46         }
47     }
48 
49     private static class ObbObserver extends OnObbStateChangeListener {
50         private String path;
51 
52         public int state = -1;
53         boolean done = false;
54 
55         @Override
onObbStateChange(String path, int state)56         public void onObbStateChange(String path, int state) {
57             Log.d(TAG, "Received message.  path=" + path + ", state=" + state);
58             synchronized (this) {
59                 this.path = path;
60                 this.state = state;
61                 done = true;
62                 notifyAll();
63             }
64         }
65 
getPath()66         public String getPath() {
67             assertTrue("Expected ObbObserver to have received a state change.", done);
68             return path;
69         }
70 
getState()71         public int getState() {
72             assertTrue("Expected ObbObserver to have received a state change.", done);
73             return state;
74         }
75 
reset()76         public void reset() {
77             this.path = null;
78             this.state = -1;
79             done = false;
80         }
81 
isDone()82         public boolean isDone() {
83             return done;
84         }
85 
waitForCompletion()86         public boolean waitForCompletion() {
87             long waitTime = 0;
88             synchronized (this) {
89                 while (!isDone() && waitTime < MAX_WAIT_TIME) {
90                     try {
91                         wait(WAIT_TIME_INCR);
92                         waitTime += WAIT_TIME_INCR;
93                     } catch (InterruptedException e) {
94                         Log.i(TAG, "Interrupted during sleep", e);
95                     }
96                 }
97             }
98 
99             return isDone();
100         }
101     }
102 
getFilePath(String name)103     private File getFilePath(String name) {
104         final File filesDir = mContext.getFilesDir();
105         final File outFile = new File(filesDir, name);
106         return outFile;
107     }
108 
copyRawToFile(int rawResId, File outFile)109     private void copyRawToFile(int rawResId, File outFile) {
110         Resources res = mContext.getResources();
111         InputStream is = null;
112         try {
113             is = res.openRawResource(rawResId);
114         } catch (NotFoundException e) {
115             fail("Failed to load resource with id: " + rawResId);
116         }
117         FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
118                 | FileUtils.S_IRWXO, -1, -1);
119         assertTrue(FileUtils.copyToFile(is, outFile));
120         FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
121                 | FileUtils.S_IRWXO, -1, -1);
122     }
123 
getStorageManager()124     private StorageManager getStorageManager() {
125         return (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE);
126     }
127 
mountObb(StorageManager sm, final int resource, final File file, int expectedState)128     private void mountObb(StorageManager sm, final int resource, final File file,
129             int expectedState) {
130         copyRawToFile(resource, file);
131 
132         final ObbObserver observer = new ObbObserver();
133         assertTrue("mountObb call on " + file.getPath() + " should succeed",
134                 sm.mountObb(file.getPath(), null, observer));
135 
136         assertTrue("Mount should have completed",
137                 observer.waitForCompletion());
138 
139         if (expectedState == OnObbStateChangeListener.MOUNTED) {
140             assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath()));
141         }
142 
143         assertEquals("Actual file and resolved file should be the same",
144                 file.getPath(), observer.getPath());
145 
146         assertEquals(expectedState, observer.getState());
147     }
148 
mountObbWithoutWait(final StorageManager sm, final int resource, final File file)149     private ObbObserver mountObbWithoutWait(final StorageManager sm, final int resource,
150             final File file) {
151         copyRawToFile(resource, file);
152 
153         final ObbObserver observer = new ObbObserver();
154         assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file
155                 .getPath(), null, observer));
156 
157         return observer;
158     }
159 
waitForObbActionCompletion(final StorageManager sm, final File file, final ObbObserver observer, int expectedState, boolean checkPath)160     private void waitForObbActionCompletion(final StorageManager sm, final File file,
161             final ObbObserver observer, int expectedState, boolean checkPath) {
162         assertTrue("Mount should have completed", observer.waitForCompletion());
163 
164         assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath()));
165 
166         if (checkPath) {
167             assertEquals("Actual file and resolved file should be the same", file.getPath(),
168                     observer.getPath());
169         }
170 
171         assertEquals(expectedState, observer.getState());
172     }
173 
checkMountedPath(final StorageManager sm, final File file)174     private String checkMountedPath(final StorageManager sm, final File file) {
175         final String mountPath = sm.getMountedObbPath(file.getPath());
176         assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX,
177                 OBB_MOUNT_PREFIX,
178                 mountPath);
179         return mountPath;
180     }
181 
unmountObb(final StorageManager sm, final File file, int expectedState)182     private void unmountObb(final StorageManager sm, final File file, int expectedState) {
183         final ObbObserver observer = new ObbObserver();
184 
185         assertTrue("unmountObb call on test1.obb should succeed",
186                 sm.unmountObb(file.getPath(), false, observer));
187 
188         assertTrue("Unmount should have completed",
189                 observer.waitForCompletion());
190 
191         assertEquals(expectedState, observer.getState());
192 
193         if (expectedState == OnObbStateChangeListener.UNMOUNTED) {
194             assertFalse("OBB should not be mounted", sm.isObbMounted(file.getPath()));
195         }
196     }
197 
198     @LargeTest
testMountAndUnmountObbNormal()199     public void testMountAndUnmountObbNormal() {
200         StorageManager sm = getStorageManager();
201 
202         final File outFile = getFilePath("test1.obb");
203 
204         mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED);
205 
206         mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
207 
208         final String mountPath = checkMountedPath(sm, outFile);
209         final File mountDir = new File(mountPath);
210 
211         assertTrue("OBB mounted path should be a directory",
212                 mountDir.isDirectory());
213 
214         unmountObb(sm, outFile, OnObbStateChangeListener.UNMOUNTED);
215     }
216 
217     @LargeTest
testAttemptMountNonObb()218     public void testAttemptMountNonObb() {
219         StorageManager sm = getStorageManager();
220 
221         final File outFile = getFilePath("test1_nosig.obb");
222 
223         try {
224             mountObb(sm, R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL);
225             fail("mountObb should've failed with an exception");
226         } catch (IllegalArgumentException e) {
227             // Expected
228         }
229 
230         assertFalse("OBB should not be mounted",
231                 sm.isObbMounted(outFile.getPath()));
232 
233         assertNull("OBB's mounted path should be null",
234                 sm.getMountedObbPath(outFile.getPath()));
235     }
236 
237     @LargeTest
testAttemptMountObbWrongPackage()238     public void testAttemptMountObbWrongPackage() {
239         StorageManager sm = getStorageManager();
240 
241         final File outFile = getFilePath("test1_wrongpackage.obb");
242 
243         mountObb(sm, R.raw.test1_wrongpackage, outFile,
244                 OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
245 
246         assertFalse("OBB should not be mounted",
247                 sm.isObbMounted(outFile.getPath()));
248 
249         assertNull("OBB's mounted path should be null",
250                 sm.getMountedObbPath(outFile.getPath()));
251     }
252 
253     @LargeTest
testMountAndUnmountTwoObbs()254     public void testMountAndUnmountTwoObbs() {
255         StorageManager sm = getStorageManager();
256 
257         final File file1 = getFilePath("test1.obb");
258         final File file2 = getFilePath("test2.obb");
259 
260         ObbObserver oo1 = mountObbWithoutWait(sm, R.raw.test1, file1);
261         ObbObserver oo2 = mountObbWithoutWait(sm, R.raw.test1, file2);
262 
263         Log.d(TAG, "Waiting for OBB #1 to complete mount");
264         waitForObbActionCompletion(sm, file1, oo1, OnObbStateChangeListener.MOUNTED, false);
265         Log.d(TAG, "Waiting for OBB #2 to complete mount");
266         waitForObbActionCompletion(sm, file2, oo2, OnObbStateChangeListener.MOUNTED, false);
267 
268         final String mountPath1 = checkMountedPath(sm, file1);
269         final File mountDir1 = new File(mountPath1);
270         assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory());
271 
272         final String mountPath2 = checkMountedPath(sm, file2);
273         final File mountDir2 = new File(mountPath2);
274         assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory());
275 
276         unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED);
277         unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED);
278     }
279 }
280