1dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey/* 2dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Copyright (C) 2014 The Android Open Source Project 3dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * 4dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 5dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * you may not use this file except in compliance with the License. 6dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * You may obtain a copy of the License at 7dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * 8dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 9dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * 10dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 11dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 12dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * See the License for the specific language governing permissions and 14dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * limitations under the License. 15dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey */ 16dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 17dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeypackage com.android.providers.downloads; 18dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 19dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport static android.net.TrafficStats.MB_IN_BYTES; 20dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport static android.provider.Downloads.Impl.STATUS_INSUFFICIENT_SPACE_ERROR; 21dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport static android.text.format.DateUtils.DAY_IN_MILLIS; 22dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport static com.android.providers.downloads.Constants.TAG; 23dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 24dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.app.DownloadManager; 25dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.content.ContentResolver; 26dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.content.ContentUris; 27dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.content.Context; 28dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.content.pm.IPackageDataObserver; 29dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.content.pm.PackageManager; 30dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.database.Cursor; 31dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.os.Environment; 32dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.provider.Downloads; 335dcbf701c9077e62ca8c1ee0079b4cbffaf57d14Elliott Hughesimport android.system.ErrnoException; 345dcbf701c9077e62ca8c1ee0079b4cbffaf57d14Elliott Hughesimport android.system.Os; 355dcbf701c9077e62ca8c1ee0079b4cbffaf57d14Elliott Hughesimport android.system.StructStat; 365dcbf701c9077e62ca8c1ee0079b4cbffaf57d14Elliott Hughesimport android.system.StructStatVfs; 37dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.text.TextUtils; 38dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport android.util.Slog; 39dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 40dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport com.android.internal.annotations.VisibleForTesting; 41dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport com.google.android.collect.Lists; 42dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport com.google.android.collect.Sets; 43dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 44dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport libcore.io.IoUtils; 45dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 46dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.io.File; 47dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.io.FileDescriptor; 48dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.io.IOException; 49dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.util.ArrayList; 50dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.util.Collections; 51dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.util.Comparator; 52dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.util.HashSet; 53dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.util.LinkedList; 54dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.util.List; 55dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.util.Objects; 56dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.util.concurrent.CountDownLatch; 57dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeyimport java.util.concurrent.TimeUnit; 58dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 59dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey/** 60dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Utility methods for managing storage space related to 61dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * {@link DownloadManager}. 62dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey */ 63dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkeypublic class StorageUtils { 64dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 65dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey /** 66dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Minimum age for a file to be considered for deletion. 67dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey */ 68dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey static final long MIN_DELETE_AGE = DAY_IN_MILLIS; 69dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 70dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey /** 71dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Reserved disk space to avoid filling disk. 72dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey */ 73dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey static final long RESERVED_BYTES = 32 * MB_IN_BYTES; 74dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 75dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey @VisibleForTesting 76dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey static boolean sForceFullEviction = false; 77dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 78dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey /** 79dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Ensure that requested free space exists on the partition backing the 80dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * given {@link FileDescriptor}. If not enough space is available, it tries 81dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * freeing up space as follows: 82dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * <ul> 83dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * <li>If backed by the data partition (including emulated external 84dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * storage), then ask {@link PackageManager} to free space from cache 85dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * directories. 86dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * <li>If backed by the cache partition, then try deleting older downloads 87dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * to free space. 88dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * </ul> 89dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey */ 90dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey public static void ensureAvailableSpace(Context context, FileDescriptor fd, long bytes) 91dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey throws IOException, StopRequestException { 92dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 93dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey long availBytes = getAvailableBytes(fd); 94dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (availBytes >= bytes) { 95dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey // Underlying partition has enough space; go ahead 96dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey return; 97dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 98dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 99dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey // Not enough space, let's try freeing some up. Start by tracking down 100dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey // the backing partition. 101dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final long dev; 102dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey try { 1035dcbf701c9077e62ca8c1ee0079b4cbffaf57d14Elliott Hughes dev = Os.fstat(fd).st_dev; 104dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } catch (ErrnoException e) { 105dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey throw e.rethrowAsIOException(); 106dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 107dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 108dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final long dataDev = getDeviceId(Environment.getDataDirectory()); 109dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final long cacheDev = getDeviceId(Environment.getDownloadCacheDirectory()); 110dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final long externalDev = getDeviceId(Environment.getExternalStorageDirectory()); 111dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 112dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (dev == dataDev || (dev == externalDev && Environment.isExternalStorageEmulated())) { 113dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey // File lives on internal storage; ask PackageManager to try freeing 114dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey // up space from cache directories. 115dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final PackageManager pm = context.getPackageManager(); 116dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final ObserverLatch observer = new ObserverLatch(); 117dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey pm.freeStorageAndNotify(sForceFullEviction ? Long.MAX_VALUE : bytes, observer); 118dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 119dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey try { 120dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (!observer.latch.await(30, TimeUnit.SECONDS)) { 121dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey throw new IOException("Timeout while freeing disk space"); 122dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 123dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } catch (InterruptedException e) { 124dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey Thread.currentThread().interrupt(); 125dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 126dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 127dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } else if (dev == cacheDev) { 128dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey // Try removing old files on cache partition 129dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey freeCacheStorage(bytes); 130dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 131dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 132dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey // Did we free enough space? 133dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey availBytes = getAvailableBytes(fd); 134dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (availBytes < bytes) { 135dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey throw new StopRequestException(STATUS_INSUFFICIENT_SPACE_ERROR, 136dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey "Not enough free space; " + bytes + " requested, " + availBytes + " available"); 137dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 138dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 139dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 140dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey /** 141dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Free requested space on cache partition, deleting oldest files first. 142dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * We're only focused on freeing up disk space, and rely on the next orphan 143dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * pass to clean up database entries. 144dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey */ 145dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey private static void freeCacheStorage(long bytes) { 146dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey // Only consider finished downloads 147dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final List<ConcreteFile> files = listFilesRecursive( 148dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey Environment.getDownloadCacheDirectory(), Constants.DIRECTORY_CACHE_RUNNING, 149dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey android.os.Process.myUid()); 150dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 151dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey Slog.d(TAG, "Found " + files.size() + " downloads on cache"); 152dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 153dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey Collections.sort(files, new Comparator<ConcreteFile>() { 154dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey @Override 155dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey public int compare(ConcreteFile lhs, ConcreteFile rhs) { 156dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey return (int) (lhs.file.lastModified() - rhs.file.lastModified()); 157dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 158dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey }); 159dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 160dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final long now = System.currentTimeMillis(); 161dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey for (ConcreteFile file : files) { 162dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (bytes <= 0) break; 163dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 164dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (now - file.file.lastModified() < MIN_DELETE_AGE) { 165dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey Slog.d(TAG, "Skipping recently modified " + file.file); 166dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } else { 167dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final long len = file.file.length(); 168dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey Slog.d(TAG, "Deleting " + file.file + " to reclaim " + len); 169dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey bytes -= len; 170dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey file.file.delete(); 171dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 172dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 173dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 174dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 175dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey /** 176dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Return number of available bytes on the filesystem backing the given 177dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * {@link FileDescriptor}, minus any {@link #RESERVED_BYTES} buffer. 178dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey */ 179dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey private static long getAvailableBytes(FileDescriptor fd) throws IOException { 180dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey try { 1815dcbf701c9077e62ca8c1ee0079b4cbffaf57d14Elliott Hughes final StructStatVfs stat = Os.fstatvfs(fd); 182dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey return (stat.f_bavail * stat.f_bsize) - RESERVED_BYTES; 183dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } catch (ErrnoException e) { 184dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey throw e.rethrowAsIOException(); 185dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 186dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 187dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 188dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey private static long getDeviceId(File file) { 189dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey try { 1905dcbf701c9077e62ca8c1ee0079b4cbffaf57d14Elliott Hughes return Os.stat(file.getAbsolutePath()).st_dev; 191dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } catch (ErrnoException e) { 192dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey // Safe since dev_t is uint 193dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey return -1; 194dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 195dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 196dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 197dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey /** 198dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Return list of all normal files under the given directory, traversing 199dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * directories recursively. 200dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * 201dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * @param exclude ignore dirs with this name, or {@code null} to ignore. 202dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * @param uid only return files owned by this UID, or {@code -1} to ignore. 203dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey */ 2041f2c2c560400ba60c5b9dfd6fd4f5e73b232803aJeff Sharkey static List<ConcreteFile> listFilesRecursive(File startDir, String exclude, int uid) { 205dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final ArrayList<ConcreteFile> files = Lists.newArrayList(); 206dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final LinkedList<File> dirs = new LinkedList<File>(); 207dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey dirs.add(startDir); 208dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey while (!dirs.isEmpty()) { 209dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final File dir = dirs.removeFirst(); 210dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (Objects.equals(dir.getName(), exclude)) continue; 211dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 212dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final File[] children = dir.listFiles(); 213dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (children == null) continue; 214dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 215dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey for (File child : children) { 216dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (child.isDirectory()) { 217dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey dirs.add(child); 218dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } else if (child.isFile()) { 219dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey try { 220dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final ConcreteFile file = new ConcreteFile(child); 221dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (uid == -1 || file.stat.st_uid == uid) { 222dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey files.add(file); 223dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 224dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } catch (ErrnoException ignored) { 225dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 226dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 227dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 228dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 229dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey return files; 230dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 231dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 232dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey /** 233dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * Concrete file on disk that has a backing device and inode. Faster than 234dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey * {@code realpath()} when looking for identical files. 235dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey */ 2361f2c2c560400ba60c5b9dfd6fd4f5e73b232803aJeff Sharkey static class ConcreteFile { 237dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey public final File file; 238dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey public final StructStat stat; 239dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 240dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey public ConcreteFile(File file) throws ErrnoException { 241dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey this.file = file; 2425dcbf701c9077e62ca8c1ee0079b4cbffaf57d14Elliott Hughes this.stat = Os.lstat(file.getAbsolutePath()); 243dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 244dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 245dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey @Override 246dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey public int hashCode() { 247dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey int result = 1; 248dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey result = 31 * result + (int) (stat.st_dev ^ (stat.st_dev >>> 32)); 249dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey result = 31 * result + (int) (stat.st_ino ^ (stat.st_ino >>> 32)); 250dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey return result; 251dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 252dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 253dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey @Override 254dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey public boolean equals(Object o) { 255dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey if (o instanceof ConcreteFile) { 256dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey final ConcreteFile f = (ConcreteFile) o; 257dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey return (f.stat.st_dev == stat.st_dev) && (f.stat.st_ino == stat.st_ino); 258dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 259dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey return false; 260dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 261dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 262dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 263dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey static class ObserverLatch extends IPackageDataObserver.Stub { 264dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey public final CountDownLatch latch = new CountDownLatch(1); 265dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey 266dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey @Override 267dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey public void onRemoveCompleted(String packageName, boolean succeeded) { 268dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey latch.countDown(); 269dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 270dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey } 271dffbb9c4567e9d29d19964a83129e38dceab7055Jeff Sharkey} 272