OSDN Git Service

Augment diskstats dumpsys to have categorization and apps.
[android-x86/frameworks-base.git] / services / core / java / com / android / server / storage / DiskStatsFileLogger.java
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
17 package com.android.server.storage;
18
19 import android.content.pm.PackageStats;
20 import android.os.Environment;
21 import android.util.ArrayMap;
22 import android.util.Log;
23
24 import com.android.server.storage.FileCollector.MeasurementResult;
25
26 import org.json.JSONArray;
27 import org.json.JSONException;
28 import org.json.JSONObject;
29
30 import java.io.File;
31 import java.io.FileNotFoundException;
32 import java.io.PrintWriter;
33 import java.util.List;
34 import java.util.Map;
35
36 /**
37  * DiskStatsFileLogger logs collected storage information to a file in a JSON format.
38  *
39  * The following information is cached in the file:
40  * 1. Size of images on disk.
41  * 2. Size of videos on disk.
42  * 3. Size of audio on disk.
43  * 4. Size of the downloads folder.
44  * 5. System size.
45  * 6. Aggregate and individual app and app cache sizes.
46  * 7. How much storage couldn't be categorized in one of the above categories.
47  */
48 public class DiskStatsFileLogger {
49     private static final String TAG = "DiskStatsLogger";
50
51     public static final String PHOTOS_KEY = "photosSize";
52     public static final String VIDEOS_KEY = "videosSize";
53     public static final String AUDIO_KEY = "audioSize";
54     public static final String DOWNLOADS_KEY = "downloadsSize";
55     public static final String SYSTEM_KEY = "systemSize";
56     public static final String MISC_KEY = "otherSize";
57     public static final String APP_SIZE_AGG_KEY = "appSize";
58     public static final String APP_CACHE_AGG_KEY = "cacheSize";
59     public static final String PACKAGE_NAMES_KEY = "packageNames";
60     public static final String APP_SIZES_KEY = "appSizes";
61     public static final String APP_CACHES_KEY = "cacheSizes";
62     public static final String LAST_QUERY_TIMESTAMP_KEY = "queryTime";
63
64     private MeasurementResult mResult;
65     private long mDownloadsSize;
66     private long mSystemSize;
67     private List<PackageStats> mPackageStats;
68
69     /**
70      * Constructs a DiskStatsFileLogger with calculated measurement results.
71      */
72     public DiskStatsFileLogger(MeasurementResult result, MeasurementResult downloadsResult,
73             List<PackageStats> stats, long systemSize) {
74         mResult = result;
75         mDownloadsSize = downloadsResult.totalAccountedSize();
76         mSystemSize = systemSize;
77         mPackageStats = stats;
78     }
79
80     /**
81      * Dumps the storage collection output to a file.
82      * @param file File to write the output into.
83      * @throws FileNotFoundException
84      */
85     public void dumpToFile(File file) throws FileNotFoundException {
86         PrintWriter pw = new PrintWriter(file);
87         JSONObject representation = getJsonRepresentation();
88         if (representation != null) {
89             pw.println(representation);
90         }
91         pw.close();
92     }
93
94     private JSONObject getJsonRepresentation() {
95         JSONObject json = new JSONObject();
96         try {
97             json.put(LAST_QUERY_TIMESTAMP_KEY, System.currentTimeMillis());
98             json.put(PHOTOS_KEY, mResult.imagesSize);
99             json.put(VIDEOS_KEY, mResult.videosSize);
100             json.put(AUDIO_KEY, mResult.audioSize);
101             json.put(DOWNLOADS_KEY, mDownloadsSize);
102             json.put(SYSTEM_KEY, mSystemSize);
103             json.put(MISC_KEY, mResult.miscSize);
104             addAppsToJson(json);
105         } catch (JSONException e) {
106             Log.e(TAG, e.toString());
107             return null;
108         }
109
110         return json;
111     }
112
113     private void addAppsToJson(JSONObject json) throws JSONException {
114         JSONArray names = new JSONArray();
115         JSONArray appSizeList = new JSONArray();
116         JSONArray cacheSizeList = new JSONArray();
117
118         long appSizeSum = 0L;
119         long cacheSizeSum = 0L;
120         boolean isExternal = Environment.isExternalStorageEmulated();
121         for (Map.Entry<String, PackageStats> entry : mergePackagesAcrossUsers().entrySet()) {
122             PackageStats stat = entry.getValue();
123             long appSize = stat.codeSize + stat.dataSize;
124             long cacheSize = stat.cacheSize;
125             if (isExternal) {
126                 appSize += stat.externalCodeSize + stat.externalDataSize;
127                 cacheSize += stat.externalCacheSize;
128             }
129             appSizeSum += appSize;
130             cacheSizeSum += cacheSize;
131
132             names.put(stat.packageName);
133             appSizeList.put(appSize);
134             cacheSizeList.put(cacheSize);
135         }
136         json.put(PACKAGE_NAMES_KEY, names);
137         json.put(APP_SIZES_KEY, appSizeList);
138         json.put(APP_CACHES_KEY, cacheSizeList);
139         json.put(APP_SIZE_AGG_KEY, appSizeSum);
140         json.put(APP_CACHE_AGG_KEY, cacheSizeSum);
141     }
142
143     /**
144      * A given package may exist for multiple users with distinct sizes. This function merges
145      * the duplicated packages together and sums up their sizes to get the actual totals for the
146      * package.
147      * @return A mapping of package name to merged package stats.
148      */
149     private ArrayMap<String, PackageStats> mergePackagesAcrossUsers() {
150         ArrayMap<String, PackageStats> packageMap = new ArrayMap<>();
151         for (PackageStats stat : mPackageStats) {
152             PackageStats existingStats = packageMap.get(stat.packageName);
153             if (existingStats != null) {
154                 existingStats.cacheSize += stat.cacheSize;
155                 existingStats.codeSize += stat.codeSize;
156                 existingStats.dataSize += stat.dataSize;
157                 existingStats.externalCacheSize += stat.externalCacheSize;
158                 existingStats.externalCodeSize += stat.externalCodeSize;
159                 existingStats.externalDataSize += stat.externalDataSize;
160             } else {
161                 packageMap.put(stat.packageName, new PackageStats(stat));
162             }
163         }
164         return packageMap;
165     }
166 }