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 android.os.storage;
18
19import android.content.Context;
20import android.os.Environment;
21import android.os.ProxyFileDescriptorCallback;
22import android.os.ParcelFileDescriptor;
23import android.system.ErrnoException;
24import android.system.Os;
25import android.test.InstrumentationTestCase;
26import android.test.suitebuilder.annotation.LargeTest;
27import android.test.suitebuilder.annotation.Suppress;
28import android.util.Log;
29
30import com.android.frameworks.coretests.R;
31import com.android.internal.os.FuseAppLoop;
32import java.io.DataInputStream;
33import java.io.IOException;
34import java.util.concurrent.ThreadFactory;
35import java.io.File;
36import java.io.FileInputStream;
37
38import junit.framework.AssertionFailedError;
39
40public class StorageManagerIntegrationTest extends StorageManagerBaseTest {
41
42    private static String LOG_TAG = "StorageManagerBaseTest.StorageManagerIntegrationTest";
43    protected File mFile = null;
44
45    /**
46     * {@inheritDoc}
47     */
48    @Override
49    public void setUp() throws Exception {
50        super.setUp();
51        mContext = getInstrumentation().getContext();
52        mFile = null;
53    }
54
55    /**
56     * {@inheritDoc}
57     */
58    @Override
59    protected void tearDown() throws Exception {
60        if (mFile != null) {
61            mFile.delete();
62            mFile = null;
63        }
64        super.tearDown();
65    }
66
67    /**
68     * Tests mounting a single OBB file and verifies its contents.
69     */
70    @LargeTest
71    public void testMountSingleObb() {
72        mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1);
73        String filePath = mFile.getAbsolutePath();
74        mountObb(filePath);
75        verifyObb1Contents(filePath);
76        unmountObb(filePath, DONT_FORCE);
77    }
78
79    /**
80     * Tests mounting several OBB files and verifies its contents.
81     */
82    @LargeTest
83    public void testMountMultipleObb() {
84        File file1 = null;
85        File file2 = null;
86        File file3 = null;
87        try {
88            file1 = createObbFile(OBB_FILE_1, R.raw.obb_file1);
89            String filePath1 = file1.getAbsolutePath();
90            mountObb(filePath1);
91            verifyObb1Contents(filePath1);
92
93            file2 = createObbFile(OBB_FILE_2, R.raw.obb_file2);
94            String filePath2 = file2.getAbsolutePath();
95            mountObb(filePath2);
96            verifyObb2Contents(filePath2);
97
98            file3 = createObbFile(OBB_FILE_3, R.raw.obb_file3);
99            String filePath3 = file3.getAbsolutePath();
100            mountObb(filePath3);
101            verifyObb3Contents(filePath3);
102
103            unmountObb(filePath1, DONT_FORCE);
104            unmountObb(filePath2, DONT_FORCE);
105            unmountObb(filePath3, DONT_FORCE);
106        } finally {
107            if (file1 != null) {
108                file1.delete();
109            }
110            if (file2 != null) {
111                file2.delete();
112            }
113            if (file3 != null) {
114                file3.delete();
115            }
116        }
117    }
118
119    /**
120     * Tests mounting a single encrypted OBB file and verifies its contents.
121     */
122    @LargeTest
123    public void testMountSingleEncryptedObb() {
124        mFile = createObbFile(OBB_FILE_3_ENCRYPTED, R.raw.obb_enc_file100_orig3);
125        String filePath = mFile.getAbsolutePath();
126        mountObb(filePath, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.MOUNTED);
127        verifyObb3Contents(filePath);
128        unmountObb(filePath, DONT_FORCE);
129    }
130
131    /**
132     * Tests mounting a single encrypted OBB file using an invalid password.
133     */
134    @LargeTest
135    @Suppress  // Failing.
136    public void testMountSingleEncryptedObbInvalidPassword() {
137        mFile = createObbFile("bad password@$%#@^*(!&)", R.raw.obb_enc_file100_orig3);
138        String filePath = mFile.getAbsolutePath();
139        mountObb(filePath, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
140        unmountObb(filePath, DONT_FORCE);
141    }
142
143    /**
144     * Tests simultaneously mounting 2 encrypted OBBs with different keys and verifies contents.
145     */
146    @LargeTest
147    public void testMountTwoEncryptedObb() {
148        File file3 = null;
149        File file1 = null;
150        try {
151            file3 = createObbFile(OBB_FILE_3_ENCRYPTED, R.raw.obb_enc_file100_orig3);
152            String filePath3 = file3.getAbsolutePath();
153            mountObb(filePath3, OBB_FILE_3_PASSWORD, OnObbStateChangeListener.MOUNTED);
154            verifyObb3Contents(filePath3);
155
156            file1 = createObbFile(OBB_FILE_1_ENCRYPTED, R.raw.obb_enc_file100_orig1);
157            String filePath1 = file1.getAbsolutePath();
158            mountObb(filePath1, OBB_FILE_1_PASSWORD, OnObbStateChangeListener.MOUNTED);
159            verifyObb1Contents(filePath1);
160
161            unmountObb(filePath3, DONT_FORCE);
162            unmountObb(filePath1, DONT_FORCE);
163        } finally {
164            if (file3 != null) {
165                file3.delete();
166            }
167            if (file1 != null) {
168                file1.delete();
169            }
170        }
171    }
172
173    /**
174     * Tests that we can not force unmount when a file is currently open on the OBB.
175     */
176    @LargeTest
177    public void testUnmount_DontForce() {
178        mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1);
179        String obbFilePath = mFile.getAbsolutePath();
180
181        MountingObbThread mountingThread = new MountingObbThread(obbFilePath,
182                OBB_FILE_1_CONTENTS_1);
183
184        try {
185            mountingThread.start();
186
187            long waitTime = 0;
188            while (!mountingThread.isFileOpenOnObb()) {
189                synchronized (mountingThread) {
190                    Log.i(LOG_TAG, "Waiting for file to be opened on OBB...");
191                    mountingThread.wait(WAIT_TIME_INCR);
192                    waitTime += WAIT_TIME_INCR;
193                    if (waitTime > MAX_WAIT_TIME) {
194                        fail("Timed out waiting for file file to be opened on OBB!");
195                    }
196                }
197            }
198
199            unmountObb(obbFilePath, DONT_FORCE);
200
201            // verify still mounted
202            assertTrue("mounted path should not be null!", obbFilePath != null);
203            assertTrue("mounted path should still be mounted!", mSm.isObbMounted(obbFilePath));
204
205            // close the opened file
206            mountingThread.doStop();
207
208            // try unmounting again (should succeed this time)
209            unmountObb(obbFilePath, DONT_FORCE);
210            assertFalse("mounted path should no longer be mounted!",
211                    mSm.isObbMounted(obbFilePath));
212        } catch (InterruptedException e) {
213            fail("Timed out waiting for file on OBB to be opened...");
214        }
215    }
216
217    /**
218     * Tests mounting a single OBB that isn't signed.
219     */
220    @LargeTest
221    public void testMountUnsignedObb() {
222        mFile = createObbFile(OBB_FILE_2_UNSIGNED, R.raw.obb_file2_nosign);
223        String filePath = mFile.getAbsolutePath();
224        mountObb(filePath, OBB_FILE_2_UNSIGNED, OnObbStateChangeListener.ERROR_INTERNAL);
225    }
226
227    /**
228     * Tests mounting a single OBB that is signed with a different package.
229     */
230    @LargeTest
231    public void testMountBadPackageNameObb() {
232        mFile = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename);
233        String filePath = mFile.getAbsolutePath();
234        mountObb(filePath, OBB_FILE_3_BAD_PACKAGENAME,
235                OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
236    }
237
238    /**
239     * Tests remounting a single OBB that has already been mounted.
240     */
241    @LargeTest
242    public void testRemountObb() {
243        mFile = createObbFile(OBB_FILE_1, R.raw.obb_file1);
244        String filePath = mFile.getAbsolutePath();
245        mountObb(filePath);
246        verifyObb1Contents(filePath);
247        mountObb(filePath, null, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
248        verifyObb1Contents(filePath);
249        unmountObb(filePath, DONT_FORCE);
250    }
251
252    @LargeTest
253    public void testOpenProxyFileDescriptor() throws Exception {
254        final ProxyFileDescriptorCallback callback = new ProxyFileDescriptorCallback() {
255            @Override
256            public long onGetSize() throws ErrnoException {
257                return 0;
258            }
259
260            @Override
261            public void onRelease() {}
262        };
263
264        final MyThreadFactory factory = new MyThreadFactory();
265        int firstMountId;
266        try (final ParcelFileDescriptor fd = mSm.openProxyFileDescriptor(
267                ParcelFileDescriptor.MODE_READ_ONLY, callback, null, factory)) {
268            assertNotSame(Thread.State.TERMINATED, factory.thread.getState());
269            firstMountId = mSm.getProxyFileDescriptorMountPointId();
270            assertNotSame(-1, firstMountId);
271        }
272
273        // After closing descriptor, the loop should terminate.
274        factory.thread.join(3000);
275        assertEquals(Thread.State.TERMINATED, factory.thread.getState());
276
277        // StorageManager should mount another bridge on the next open request.
278        try (final ParcelFileDescriptor fd = mSm.openProxyFileDescriptor(
279                ParcelFileDescriptor.MODE_WRITE_ONLY, callback, null, factory)) {
280            assertNotSame(Thread.State.TERMINATED, factory.thread.getState());
281            assertNotSame(firstMountId, mSm.getProxyFileDescriptorMountPointId());
282        }
283    }
284
285    private static class MyThreadFactory implements ThreadFactory {
286        Thread thread = null;
287
288        @Override
289        public Thread newThread(Runnable r) {
290            thread = new Thread(r);
291            return thread;
292        }
293    }
294}