FileCollector.java revision e4bea2d769ae9ab3df028886d61e1215e43fab6f
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE2.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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.server.storage;
18
19import android.annotation.IntDef;
20import android.app.usage.ExternalStorageStats;
21import android.app.usage.StorageStatsManager;
22import android.content.Context;
23import android.content.pm.PackageManager;
24import android.os.UserHandle;
25import android.os.storage.StorageManager;
26import android.os.storage.VolumeInfo;
27import android.util.ArrayMap;
28
29import java.io.File;
30import java.io.IOException;
31import java.lang.annotation.Retention;
32import java.lang.annotation.RetentionPolicy;
33import java.util.Map;
34
35/**
36 * FileCollector walks over a directory and categorizes storage usage by their type.
37 */
38public class FileCollector {
39    private static final int UNRECOGNIZED = -1;
40    private static final int IMAGES = 0;
41    private static final int VIDEO = 1;
42    private static final int AUDIO = 2;
43    @Retention(RetentionPolicy.SOURCE)
44    @IntDef({
45            UNRECOGNIZED,
46            IMAGES,
47            VIDEO,
48            AUDIO })
49    private @interface FileTypes {}
50
51
52    private static final Map<String, Integer> EXTENSION_MAP = new ArrayMap<String, Integer>();
53    static {
54        // Audio
55        EXTENSION_MAP.put("aac", AUDIO);
56        EXTENSION_MAP.put("amr", AUDIO);
57        EXTENSION_MAP.put("awb", AUDIO);
58        EXTENSION_MAP.put("snd", AUDIO);
59        EXTENSION_MAP.put("flac", AUDIO);
60        EXTENSION_MAP.put("mp3", AUDIO);
61        EXTENSION_MAP.put("mpga", AUDIO);
62        EXTENSION_MAP.put("mpega", AUDIO);
63        EXTENSION_MAP.put("mp2", AUDIO);
64        EXTENSION_MAP.put("m4a", AUDIO);
65        EXTENSION_MAP.put("aif", AUDIO);
66        EXTENSION_MAP.put("aiff", AUDIO);
67        EXTENSION_MAP.put("aifc", AUDIO);
68        EXTENSION_MAP.put("gsm", AUDIO);
69        EXTENSION_MAP.put("mka", AUDIO);
70        EXTENSION_MAP.put("m3u", AUDIO);
71        EXTENSION_MAP.put("wma", AUDIO);
72        EXTENSION_MAP.put("wax", AUDIO);
73        EXTENSION_MAP.put("ra", AUDIO);
74        EXTENSION_MAP.put("rm", AUDIO);
75        EXTENSION_MAP.put("ram", AUDIO);
76        EXTENSION_MAP.put("pls", AUDIO);
77        EXTENSION_MAP.put("sd2", AUDIO);
78        EXTENSION_MAP.put("wav", AUDIO);
79        EXTENSION_MAP.put("ogg", AUDIO);
80        EXTENSION_MAP.put("oga", AUDIO);
81        // Video
82        EXTENSION_MAP.put("3gpp", VIDEO);
83        EXTENSION_MAP.put("3gp", VIDEO);
84        EXTENSION_MAP.put("3gpp2", VIDEO);
85        EXTENSION_MAP.put("3g2", VIDEO);
86        EXTENSION_MAP.put("avi", VIDEO);
87        EXTENSION_MAP.put("dl", VIDEO);
88        EXTENSION_MAP.put("dif", VIDEO);
89        EXTENSION_MAP.put("dv", VIDEO);
90        EXTENSION_MAP.put("fli", VIDEO);
91        EXTENSION_MAP.put("m4v", VIDEO);
92        EXTENSION_MAP.put("ts", VIDEO);
93        EXTENSION_MAP.put("mpeg", VIDEO);
94        EXTENSION_MAP.put("mpg", VIDEO);
95        EXTENSION_MAP.put("mpe", VIDEO);
96        EXTENSION_MAP.put("mp4", VIDEO);
97        EXTENSION_MAP.put("vob", VIDEO);
98        EXTENSION_MAP.put("qt", VIDEO);
99        EXTENSION_MAP.put("mov", VIDEO);
100        EXTENSION_MAP.put("mxu", VIDEO);
101        EXTENSION_MAP.put("webm", VIDEO);
102        EXTENSION_MAP.put("lsf", VIDEO);
103        EXTENSION_MAP.put("lsx", VIDEO);
104        EXTENSION_MAP.put("mkv", VIDEO);
105        EXTENSION_MAP.put("mng", VIDEO);
106        EXTENSION_MAP.put("asf", VIDEO);
107        EXTENSION_MAP.put("asx", VIDEO);
108        EXTENSION_MAP.put("wm", VIDEO);
109        EXTENSION_MAP.put("wmv", VIDEO);
110        EXTENSION_MAP.put("wmx", VIDEO);
111        EXTENSION_MAP.put("wvx", VIDEO);
112        EXTENSION_MAP.put("movie", VIDEO);
113        EXTENSION_MAP.put("wrf", VIDEO);
114        // Images
115        EXTENSION_MAP.put("bmp", IMAGES);
116        EXTENSION_MAP.put("gif", IMAGES);
117        EXTENSION_MAP.put("jpg", IMAGES);
118        EXTENSION_MAP.put("jpeg", IMAGES);
119        EXTENSION_MAP.put("jpe", IMAGES);
120        EXTENSION_MAP.put("pcx", IMAGES);
121        EXTENSION_MAP.put("png", IMAGES);
122        EXTENSION_MAP.put("svg", IMAGES);
123        EXTENSION_MAP.put("svgz", IMAGES);
124        EXTENSION_MAP.put("tiff", IMAGES);
125        EXTENSION_MAP.put("tif", IMAGES);
126        EXTENSION_MAP.put("wbmp", IMAGES);
127        EXTENSION_MAP.put("webp", IMAGES);
128        EXTENSION_MAP.put("dng", IMAGES);
129        EXTENSION_MAP.put("cr2", IMAGES);
130        EXTENSION_MAP.put("ras", IMAGES);
131        EXTENSION_MAP.put("art", IMAGES);
132        EXTENSION_MAP.put("jng", IMAGES);
133        EXTENSION_MAP.put("nef", IMAGES);
134        EXTENSION_MAP.put("nrw", IMAGES);
135        EXTENSION_MAP.put("orf", IMAGES);
136        EXTENSION_MAP.put("rw2", IMAGES);
137        EXTENSION_MAP.put("pef", IMAGES);
138        EXTENSION_MAP.put("psd", IMAGES);
139        EXTENSION_MAP.put("pnm", IMAGES);
140        EXTENSION_MAP.put("pbm", IMAGES);
141        EXTENSION_MAP.put("pgm", IMAGES);
142        EXTENSION_MAP.put("ppm", IMAGES);
143        EXTENSION_MAP.put("srw", IMAGES);
144        EXTENSION_MAP.put("arw", IMAGES);
145        EXTENSION_MAP.put("rgb", IMAGES);
146        EXTENSION_MAP.put("xbm", IMAGES);
147        EXTENSION_MAP.put("xpm", IMAGES);
148        EXTENSION_MAP.put("xwd", IMAGES);
149    }
150
151    /**
152     * Returns the file categorization measurement result.
153     * @param path Directory to collect and categorize storage in.
154     */
155    public static MeasurementResult getMeasurementResult(File path) {
156        return collectFiles(StorageManager.maybeTranslateEmulatedPathToInternal(path),
157                new MeasurementResult());
158    }
159
160    /**
161     * Returns the file categorization result for the primary internal storage UUID.
162     *
163     * @param context
164     */
165    public static MeasurementResult getMeasurementResult(Context context) {
166        MeasurementResult result = new MeasurementResult();
167        StorageStatsManager ssm =
168                (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE);
169        ExternalStorageStats stats = null;
170        try {
171            stats =
172                    ssm.queryExternalStatsForUser(
173                            StorageManager.UUID_PRIVATE_INTERNAL,
174                            UserHandle.of(context.getUserId()));
175            result.imagesSize = stats.getImageBytes();
176            result.videosSize = stats.getVideoBytes();
177            result.audioSize = stats.getAudioBytes();
178            result.miscSize =
179                    stats.getTotalBytes()
180                            - result.imagesSize
181                            - result.videosSize
182                            - result.audioSize;
183        } catch (IOException e) {
184            throw new IllegalStateException("Could not query storage");
185        }
186
187        return result;
188    }
189
190    /**
191     * Returns the size of a system for a given context. This is done by finding the difference
192     * between the shared data and the total primary storage size.
193     *
194     * @param context Context to use to get storage information.
195     */
196    public static long getSystemSize(Context context) {
197        PackageManager pm = context.getPackageManager();
198        VolumeInfo primaryVolume = pm.getPrimaryStorageCurrentVolume();
199
200        StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
201        VolumeInfo shared = sm.findEmulatedForPrivate(primaryVolume);
202        if (shared == null) {
203            return 0;
204        }
205
206        // In some cases, the path may be null -- we can't determine the size in this case.
207        final File sharedPath = shared.getPath();
208        if (sharedPath == null) {
209          return 0;
210        }
211
212        final long sharedDataSize = sharedPath.getTotalSpace();
213        long systemSize = sm.getPrimaryStorageSize() - sharedDataSize;
214
215        // This case is not exceptional -- we just fallback to the shared data volume in this case.
216        if (systemSize <= 0) {
217            return 0;
218        }
219
220        return systemSize;
221    }
222
223    private static MeasurementResult collectFiles(File file, MeasurementResult result) {
224        File[] files = file.listFiles();
225
226        if (files == null) {
227            return result;
228        }
229
230        for (File f : files) {
231            if (f.isDirectory()) {
232                try {
233                    collectFiles(f, result);
234                } catch (StackOverflowError e) {
235                    return result;
236                }
237            } else {
238                handleFile(result, f);
239            }
240        }
241
242        return result;
243    }
244
245    private static void handleFile(MeasurementResult result, File f) {
246        long fileSize = f.length();
247        int fileType = EXTENSION_MAP.getOrDefault(getExtensionForFile(f), UNRECOGNIZED);
248        switch (fileType) {
249            case AUDIO:
250                result.audioSize += fileSize;
251                break;
252            case VIDEO:
253                result.videosSize += fileSize;
254                break;
255            case IMAGES:
256                result.imagesSize += fileSize;
257                break;
258            default:
259                result.miscSize += fileSize;
260        }
261    }
262
263    private static String getExtensionForFile(File file) {
264        String fileName = file.getName();
265        int index = fileName.lastIndexOf('.');
266        if (index == -1) {
267            return "";
268        }
269        return fileName.substring(index + 1).toLowerCase();
270    }
271
272    /**
273     * MeasurementResult contains a storage categorization result.
274     */
275    public static class MeasurementResult {
276        public long imagesSize;
277        public long videosSize;
278        public long miscSize;
279        public long audioSize;
280
281        /**
282         * Sums up the storage taken by all of the categorizable sizes in the measurement.
283         */
284        public long totalAccountedSize() {
285            return imagesSize + videosSize + miscSize + audioSize;
286        }
287    }
288}
289