StorageTest.java revision dffbb9c4567e9d29d19964a83129e38dceab7055
1/* 2 * Copyright (C) 2014 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.providers.downloads; 18 19import static android.app.DownloadManager.COLUMN_REASON; 20import static android.app.DownloadManager.ERROR_INSUFFICIENT_SPACE; 21import static android.app.DownloadManager.STATUS_FAILED; 22import static android.app.DownloadManager.STATUS_SUCCESSFUL; 23import static android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION; 24import static android.provider.Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION; 25 26import android.app.DownloadManager; 27import android.content.pm.PackageManager; 28import android.os.Environment; 29import android.os.StatFs; 30import android.provider.Downloads.Impl; 31import android.test.MoreAsserts; 32import android.util.Log; 33 34import com.android.providers.downloads.StorageUtils.ObserverLatch; 35import com.google.mockwebserver.MockResponse; 36import com.google.mockwebserver.SocketPolicy; 37 38import libcore.io.ErrnoException; 39import libcore.io.ForwardingOs; 40import libcore.io.IoUtils; 41import libcore.io.Libcore; 42import libcore.io.Os; 43import libcore.io.StructStatVfs; 44 45import java.io.File; 46import java.io.FileDescriptor; 47import java.io.FileOutputStream; 48import java.io.IOException; 49import java.util.concurrent.TimeUnit; 50 51public class StorageTest extends AbstractPublicApiTest { 52 private static final String TAG = "StorageTest"; 53 54 private static final int DOWNLOAD_SIZE = 512 * 1024; 55 private static final byte[] DOWNLOAD_BODY; 56 57 static { 58 DOWNLOAD_BODY = new byte[DOWNLOAD_SIZE]; 59 for (int i = 0; i < DOWNLOAD_SIZE; i++) { 60 DOWNLOAD_BODY[i] = (byte) (i % 32); 61 } 62 } 63 64 private Os mOriginal; 65 private long mStealBytes; 66 67 public StorageTest() { 68 super(new FakeSystemFacade()); 69 } 70 71 @Override 72 protected void setUp() throws Exception { 73 super.setUp(); 74 75 StorageUtils.sForceFullEviction = true; 76 mStealBytes = 0; 77 78 mOriginal = Libcore.os; 79 Libcore.os = new ForwardingOs(mOriginal) { 80 @Override 81 public StructStatVfs statvfs(String path) throws ErrnoException { 82 return stealBytes(os.statvfs(path)); 83 } 84 85 @Override 86 public StructStatVfs fstatvfs(FileDescriptor fd) throws ErrnoException { 87 return stealBytes(os.fstatvfs(fd)); 88 } 89 90 private StructStatVfs stealBytes(StructStatVfs s) { 91 final long stealBlocks = (mStealBytes + (s.f_bsize - 1)) / s.f_bsize; 92 final long f_bavail = s.f_bavail - stealBlocks; 93 return new StructStatVfs(s.f_bsize, s.f_frsize, s.f_blocks, s.f_bfree, f_bavail, 94 s.f_files, s.f_ffree, s.f_favail, s.f_fsid, s.f_flag, s.f_namemax); 95 } 96 }; 97 } 98 99 @Override 100 protected void tearDown() throws Exception { 101 super.tearDown(); 102 103 StorageUtils.sForceFullEviction = false; 104 mStealBytes = 0; 105 106 if (mOriginal != null) { 107 Libcore.os = mOriginal; 108 } 109 } 110 111 private enum CacheStatus { CLEAN, DIRTY } 112 private enum BodyType { COMPLETE, CHUNKED } 113 114 public void testDataDirtyComplete() throws Exception { 115 prepareAndRunDownload(DESTINATION_CACHE_PARTITION, 116 CacheStatus.DIRTY, BodyType.COMPLETE, 117 STATUS_SUCCESSFUL, -1); 118 } 119 120 public void testDataDirtyChunked() throws Exception { 121 prepareAndRunDownload(DESTINATION_CACHE_PARTITION, 122 CacheStatus.DIRTY, BodyType.CHUNKED, 123 STATUS_SUCCESSFUL, -1); 124 } 125 126 public void testDataCleanComplete() throws Exception { 127 prepareAndRunDownload(DESTINATION_CACHE_PARTITION, 128 CacheStatus.CLEAN, BodyType.COMPLETE, 129 STATUS_FAILED, ERROR_INSUFFICIENT_SPACE); 130 } 131 132 public void testDataCleanChunked() throws Exception { 133 prepareAndRunDownload(DESTINATION_CACHE_PARTITION, 134 CacheStatus.CLEAN, BodyType.CHUNKED, 135 STATUS_FAILED, ERROR_INSUFFICIENT_SPACE); 136 } 137 138 public void testCacheDirtyComplete() throws Exception { 139 prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION, 140 CacheStatus.DIRTY, BodyType.COMPLETE, 141 STATUS_SUCCESSFUL, -1); 142 } 143 144 public void testCacheDirtyChunked() throws Exception { 145 prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION, 146 CacheStatus.DIRTY, BodyType.CHUNKED, 147 STATUS_SUCCESSFUL, -1); 148 } 149 150 public void testCacheCleanComplete() throws Exception { 151 prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION, 152 CacheStatus.CLEAN, BodyType.COMPLETE, 153 STATUS_FAILED, ERROR_INSUFFICIENT_SPACE); 154 } 155 156 public void testCacheCleanChunked() throws Exception { 157 prepareAndRunDownload(DESTINATION_SYSTEMCACHE_PARTITION, 158 CacheStatus.CLEAN, BodyType.CHUNKED, 159 STATUS_FAILED, ERROR_INSUFFICIENT_SPACE); 160 } 161 162 private void prepareAndRunDownload( 163 int dest, CacheStatus cache, BodyType body, int expectedStatus, int expectedReason) 164 throws Exception { 165 166 // Ensure that we've purged everything possible for destination 167 final File dirtyDir; 168 if (dest == DESTINATION_CACHE_PARTITION) { 169 final PackageManager pm = getContext().getPackageManager(); 170 final ObserverLatch observer = new ObserverLatch(); 171 pm.freeStorageAndNotify(Long.MAX_VALUE, observer); 172 173 try { 174 if (!observer.latch.await(30, TimeUnit.SECONDS)) { 175 throw new IOException("Timeout while freeing disk space"); 176 } 177 } catch (InterruptedException e) { 178 Thread.currentThread().interrupt(); 179 } 180 181 dirtyDir = getContext().getCacheDir(); 182 183 } else if (dest == DESTINATION_SYSTEMCACHE_PARTITION) { 184 IoUtils.deleteContents(Environment.getDownloadCacheDirectory()); 185 dirtyDir = Environment.getDownloadCacheDirectory(); 186 187 } else { 188 throw new IllegalArgumentException("Unknown destination"); 189 } 190 191 // Allocate a cache file, if requested, making it large enough and old 192 // enough to clear. 193 final File dirtyFile; 194 if (cache == CacheStatus.DIRTY) { 195 dirtyFile = new File(dirtyDir, "cache_file.bin"); 196 assertTrue(dirtyFile.createNewFile()); 197 final FileOutputStream os = new FileOutputStream(dirtyFile); 198 final int dirtySize = (DOWNLOAD_SIZE * 3) / 2; 199 Libcore.os.posix_fallocate(os.getFD(), 0, dirtySize); 200 IoUtils.closeQuietly(os); 201 202 dirtyFile.setLastModified( 203 System.currentTimeMillis() - (StorageUtils.MIN_DELETE_AGE * 2)); 204 } else { 205 dirtyFile = null; 206 } 207 208 // At this point, hide all other disk space to make the download fail; 209 // if we have a dirty cache file it can be cleared to let us proceed. 210 final long targetFree = StorageUtils.RESERVED_BYTES + (DOWNLOAD_SIZE / 2); 211 212 final StatFs stat = new StatFs(dirtyDir.getAbsolutePath()); 213 Log.d(TAG, "Available bytes (before steal): " + stat.getAvailableBytes()); 214 mStealBytes = stat.getAvailableBytes() - targetFree; 215 216 stat.restat(dirtyDir.getAbsolutePath()); 217 Log.d(TAG, "Available bytes (after steal): " + stat.getAvailableBytes()); 218 219 final MockResponse resp = new MockResponse().setResponseCode(200) 220 .setHeader("Content-type", "text/plain") 221 .setSocketPolicy(SocketPolicy.DISCONNECT_AT_END); 222 if (body == BodyType.CHUNKED) { 223 resp.setChunkedBody(DOWNLOAD_BODY, 1021); 224 } else { 225 resp.setBody(DOWNLOAD_BODY); 226 } 227 enqueueResponse(resp); 228 229 final DownloadManager.Request req = getRequest(); 230 if (dest == Impl.DESTINATION_SYSTEMCACHE_PARTITION) { 231 req.setDestinationToSystemCache(); 232 } 233 final Download download = enqueueRequest(req); 234 download.runUntilStatus(expectedStatus); 235 236 if (expectedStatus == STATUS_SUCCESSFUL) { 237 MoreAsserts.assertEquals(DOWNLOAD_BODY, download.getRawContents()); 238 } 239 240 if (expectedReason != -1) { 241 assertEquals(expectedReason, download.getLongField(COLUMN_REASON)); 242 } 243 244 if (dirtyFile != null) { 245 assertFalse(dirtyFile.exists()); 246 } 247 } 248} 249