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 android.content.Context;
20import android.content.res.Resources;
21import android.content.res.Resources.NotFoundException;
22import android.os.FileUtils;
23import android.os.storage.OnObbStateChangeListener;
24import android.os.storage.StorageManager;
25import android.test.AndroidTestCase;
26import android.test.ComparisonFailure;
27import android.test.suitebuilder.annotation.LargeTest;
28import android.util.Log;
29
30import com.android.frameworks.servicestests.R;
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    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
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
66        public String getPath() {
67            assertTrue("Expected ObbObserver to have received a state change.", done);
68            return path;
69        }
70
71        public int getState() {
72            assertTrue("Expected ObbObserver to have received a state change.", done);
73            return state;
74        }
75
76        public void reset() {
77            this.path = null;
78            this.state = -1;
79            done = false;
80        }
81
82        public boolean isDone() {
83            return done;
84        }
85
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
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
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
124    private StorageManager getStorageManager() {
125        return (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE);
126    }
127
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
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
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
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
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
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
218    public void testAttemptMountNonObb() {
219        StorageManager sm = getStorageManager();
220
221        final File outFile = getFilePath("test1_nosig.obb");
222
223        mountObb(sm, R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL);
224
225        assertFalse("OBB should not be mounted",
226                sm.isObbMounted(outFile.getPath()));
227
228        assertNull("OBB's mounted path should be null",
229                sm.getMountedObbPath(outFile.getPath()));
230    }
231
232    @LargeTest
233    public void testAttemptMountObbWrongPackage() {
234        StorageManager sm = getStorageManager();
235
236        final File outFile = getFilePath("test1_wrongpackage.obb");
237
238        mountObb(sm, R.raw.test1_wrongpackage, outFile,
239                OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
240
241        assertFalse("OBB should not be mounted",
242                sm.isObbMounted(outFile.getPath()));
243
244        assertNull("OBB's mounted path should be null",
245                sm.getMountedObbPath(outFile.getPath()));
246    }
247
248    @LargeTest
249    public void testMountAndUnmountTwoObbs() {
250        StorageManager sm = getStorageManager();
251
252        final File file1 = getFilePath("test1.obb");
253        final File file2 = getFilePath("test2.obb");
254
255        ObbObserver oo1 = mountObbWithoutWait(sm, R.raw.test1, file1);
256        ObbObserver oo2 = mountObbWithoutWait(sm, R.raw.test1, file2);
257
258        Log.d(TAG, "Waiting for OBB #1 to complete mount");
259        waitForObbActionCompletion(sm, file1, oo1, OnObbStateChangeListener.MOUNTED, false);
260        Log.d(TAG, "Waiting for OBB #2 to complete mount");
261        waitForObbActionCompletion(sm, file2, oo2, OnObbStateChangeListener.MOUNTED, false);
262
263        final String mountPath1 = checkMountedPath(sm, file1);
264        final File mountDir1 = new File(mountPath1);
265        assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory());
266
267        final String mountPath2 = checkMountedPath(sm, file2);
268        final File mountDir2 = new File(mountPath2);
269        assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory());
270
271        unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED);
272        unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED);
273    }
274}
275