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