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 17 package com.android.providers.downloads; 18 19 import android.app.DownloadManager; 20 import android.system.ErrnoException; 21 import android.system.Os; 22 import android.system.StructStat; 23 24 import com.google.android.collect.Lists; 25 26 import java.io.File; 27 import java.util.ArrayList; 28 import java.util.LinkedList; 29 import java.util.List; 30 import java.util.Objects; 31 32 /** 33 * Utility methods for managing storage space related to 34 * {@link DownloadManager}. 35 */ 36 public class StorageUtils { 37 /** 38 * Return list of all normal files under the given directory, traversing 39 * directories recursively. 40 * 41 * @param exclude ignore dirs with this name, or {@code null} to ignore. 42 * @param uid only return files owned by this UID, or {@code -1} to ignore. 43 */ listFilesRecursive(File startDir, String exclude, int uid)44 static List<ConcreteFile> listFilesRecursive(File startDir, String exclude, int uid) { 45 final ArrayList<ConcreteFile> files = Lists.newArrayList(); 46 final LinkedList<File> dirs = new LinkedList<File>(); 47 dirs.add(startDir); 48 while (!dirs.isEmpty()) { 49 final File dir = dirs.removeFirst(); 50 if (Objects.equals(dir.getName(), exclude)) continue; 51 52 final File[] children = dir.listFiles(); 53 if (children == null) continue; 54 55 for (File child : children) { 56 if (child.isDirectory()) { 57 dirs.add(child); 58 } else if (child.isFile()) { 59 try { 60 final ConcreteFile file = new ConcreteFile(child); 61 if (uid == -1 || file.stat.st_uid == uid) { 62 files.add(file); 63 } 64 } catch (ErrnoException ignored) { 65 } 66 } 67 } 68 } 69 return files; 70 } 71 72 /** 73 * Concrete file on disk that has a backing device and inode. Faster than 74 * {@code realpath()} when looking for identical files. 75 */ 76 static class ConcreteFile { 77 public final File file; 78 public final StructStat stat; 79 ConcreteFile(File file)80 public ConcreteFile(File file) throws ErrnoException { 81 this.file = file; 82 this.stat = Os.lstat(file.getAbsolutePath()); 83 } 84 85 @Override hashCode()86 public int hashCode() { 87 int result = 1; 88 result = 31 * result + (int) (stat.st_dev ^ (stat.st_dev >>> 32)); 89 result = 31 * result + (int) (stat.st_ino ^ (stat.st_ino >>> 32)); 90 return result; 91 } 92 93 @Override equals(Object o)94 public boolean equals(Object o) { 95 if (o instanceof ConcreteFile) { 96 final ConcreteFile f = (ConcreteFile) o; 97 return (f.stat.st_dev == stat.st_dev) && (f.stat.st_ino == stat.st_ino); 98 } 99 return false; 100 } 101 } 102 } 103