Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 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 | |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 19 | import android.content.Context; |
| 20 | import android.content.res.Resources; |
| 21 | import android.content.res.Resources.NotFoundException; |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 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 | |
Jeff Sharkey | 4fbbda4 | 2012-09-24 18:34:07 -0700 | [diff] [blame] | 30 | import com.android.frameworks.servicestests.R; |
| 31 | |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 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 | |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 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 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 49 | private static class ObbObserver extends OnObbStateChangeListener { |
| 50 | private String path; |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 51 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 52 | public int state = -1; |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 53 | boolean done = false; |
| 54 | |
| 55 | @Override |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 56 | public void onObbStateChange(String path, int state) { |
| 57 | Log.d(TAG, "Received message. path=" + path + ", state=" + state); |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 58 | synchronized (this) { |
| 59 | this.path = path; |
| 60 | this.state = state; |
| 61 | done = true; |
| 62 | notifyAll(); |
| 63 | } |
| 64 | } |
| 65 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 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 | |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 76 | public void reset() { |
| 77 | this.path = null; |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 78 | this.state = -1; |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 79 | done = false; |
| 80 | } |
| 81 | |
| 82 | public boolean isDone() { |
| 83 | return done; |
| 84 | } |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 85 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 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 | } |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 96 | } |
| 97 | } |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 98 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 99 | return isDone(); |
| 100 | } |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 101 | } |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 102 | |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 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, |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 129 | int expectedState) { |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 130 | copyRawToFile(resource, file); |
| 131 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 132 | final ObbObserver observer = new ObbObserver(); |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 133 | assertTrue("mountObb call on " + file.getPath() + " should succeed", |
| 134 | sm.mountObb(file.getPath(), null, observer)); |
| 135 | |
| 136 | assertTrue("Mount should have completed", |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 137 | observer.waitForCompletion()); |
| 138 | |
| 139 | if (expectedState == OnObbStateChangeListener.MOUNTED) { |
| 140 | assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath())); |
| 141 | } |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 142 | |
| 143 | assertEquals("Actual file and resolved file should be the same", |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 144 | file.getPath(), observer.getPath()); |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 145 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 146 | assertEquals(expectedState, observer.getState()); |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 147 | } |
| 148 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 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) { |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 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 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 182 | private void unmountObb(final StorageManager sm, final File file, int expectedState) { |
| 183 | final ObbObserver observer = new ObbObserver(); |
| 184 | |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 185 | assertTrue("unmountObb call on test1.obb should succeed", |
Xiaohui Chen | 621b3fc | 2015-10-02 14:41:42 -0700 | [diff] [blame] | 186 | sm.unmountObb(file.getPath(), false, observer)); |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 187 | |
| 188 | assertTrue("Unmount should have completed", |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 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 | } |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | @LargeTest |
| 199 | public void testMountAndUnmountObbNormal() { |
| 200 | StorageManager sm = getStorageManager(); |
| 201 | |
| 202 | final File outFile = getFilePath("test1.obb"); |
| 203 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 204 | mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED); |
| 205 | |
| 206 | mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 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 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 214 | unmountObb(sm, outFile, OnObbStateChangeListener.UNMOUNTED); |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 215 | } |
| 216 | |
| 217 | @LargeTest |
| 218 | public void testAttemptMountNonObb() { |
| 219 | StorageManager sm = getStorageManager(); |
| 220 | |
| 221 | final File outFile = getFilePath("test1_nosig.obb"); |
| 222 | |
Sudheer Shanka | 25469aa | 2018-08-27 15:50:23 -0700 | [diff] [blame] | 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 | } |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 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 |
| 238 | public void testAttemptMountObbWrongPackage() { |
| 239 | StorageManager sm = getStorageManager(); |
| 240 | |
| 241 | final File outFile = getFilePath("test1_wrongpackage.obb"); |
| 242 | |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 243 | mountObb(sm, R.raw.test1_wrongpackage, outFile, |
| 244 | OnObbStateChangeListener.ERROR_PERMISSION_DENIED); |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 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 | } |
Kenny Root | af9d667 | 2010-10-08 09:21:39 -0700 | [diff] [blame] | 252 | |
| 253 | @LargeTest |
| 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 | } |
Kenny Root | 17eb6fb | 2010-10-06 15:02:52 -0700 | [diff] [blame] | 279 | } |