177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi/* 277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * Copyright (C) 2016 The Android Open Source Project 377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * Licensed under the Apache License, Version 2.0 (the "License"); you may not 577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * use this file except in compliance with the License. You may obtain a copy of 677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * the License at 777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * http://www.apache.org/licenses/LICENSE2.0 977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 1077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * Unless required by applicable law or agreed to in writing, software 1177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 1277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 1377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * License for the specific language governing permissions and limitations under 1477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * the License. 1577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi */ 1677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 1777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishipackage com.android.server.storage; 1877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 1977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport android.content.pm.PackageStats; 2077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport android.os.Environment; 21e47eac74f7c80738017734342f66b615189e7596Daniel Nishiimport android.os.UserHandle; 2277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport android.util.ArrayMap; 2377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport android.util.Log; 2477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 2577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport com.android.server.storage.FileCollector.MeasurementResult; 2677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 2777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport org.json.JSONArray; 2877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport org.json.JSONException; 2977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport org.json.JSONObject; 3077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 3177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport java.io.File; 3277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport java.io.FileNotFoundException; 3377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport java.io.PrintWriter; 3477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport java.util.List; 3577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishiimport java.util.Map; 3677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 3777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi/** 3877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * DiskStatsFileLogger logs collected storage information to a file in a JSON format. 3977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 4077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * The following information is cached in the file: 4177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 1. Size of images on disk. 4277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 2. Size of videos on disk. 4377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 3. Size of audio on disk. 4477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 4. Size of the downloads folder. 4577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 5. System size. 4677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 6. Aggregate and individual app and app cache sizes. 4777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * 7. How much storage couldn't be categorized in one of the above categories. 4877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi */ 4977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishipublic class DiskStatsFileLogger { 5077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi private static final String TAG = "DiskStatsLogger"; 5177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 5277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String PHOTOS_KEY = "photosSize"; 5377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String VIDEOS_KEY = "videosSize"; 5477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String AUDIO_KEY = "audioSize"; 5577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String DOWNLOADS_KEY = "downloadsSize"; 5677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String SYSTEM_KEY = "systemSize"; 5777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String MISC_KEY = "otherSize"; 5877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String APP_SIZE_AGG_KEY = "appSize"; 5977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String APP_CACHE_AGG_KEY = "cacheSize"; 6077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String PACKAGE_NAMES_KEY = "packageNames"; 6177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String APP_SIZES_KEY = "appSizes"; 6277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String APP_CACHES_KEY = "cacheSizes"; 6377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public static final String LAST_QUERY_TIMESTAMP_KEY = "queryTime"; 6477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 6577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi private MeasurementResult mResult; 6677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi private long mDownloadsSize; 6777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi private long mSystemSize; 6877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi private List<PackageStats> mPackageStats; 6977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 7077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi /** 7177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * Constructs a DiskStatsFileLogger with calculated measurement results. 7277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi */ 7377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public DiskStatsFileLogger(MeasurementResult result, MeasurementResult downloadsResult, 7477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi List<PackageStats> stats, long systemSize) { 7577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi mResult = result; 7677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi mDownloadsSize = downloadsResult.totalAccountedSize(); 7777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi mSystemSize = systemSize; 7877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi mPackageStats = stats; 7977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 8077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 8177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi /** 8277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * Dumps the storage collection output to a file. 8377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * @param file File to write the output into. 8477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * @throws FileNotFoundException 8577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi */ 8677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi public void dumpToFile(File file) throws FileNotFoundException { 8777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi PrintWriter pw = new PrintWriter(file); 8877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi JSONObject representation = getJsonRepresentation(); 8977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi if (representation != null) { 9077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi pw.println(representation); 9177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 9277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi pw.close(); 9377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 9477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 9577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi private JSONObject getJsonRepresentation() { 9677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi JSONObject json = new JSONObject(); 9777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi try { 9877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(LAST_QUERY_TIMESTAMP_KEY, System.currentTimeMillis()); 9977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(PHOTOS_KEY, mResult.imagesSize); 10077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(VIDEOS_KEY, mResult.videosSize); 10177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(AUDIO_KEY, mResult.audioSize); 10277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(DOWNLOADS_KEY, mDownloadsSize); 10377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(SYSTEM_KEY, mSystemSize); 10477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(MISC_KEY, mResult.miscSize); 10577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi addAppsToJson(json); 10677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } catch (JSONException e) { 10777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi Log.e(TAG, e.toString()); 10877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi return null; 10977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 11077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 11177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi return json; 11277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 11377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 11477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi private void addAppsToJson(JSONObject json) throws JSONException { 11577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi JSONArray names = new JSONArray(); 11677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi JSONArray appSizeList = new JSONArray(); 11777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi JSONArray cacheSizeList = new JSONArray(); 11877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 11977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi long appSizeSum = 0L; 12077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi long cacheSizeSum = 0L; 12177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi boolean isExternal = Environment.isExternalStorageEmulated(); 122e47eac74f7c80738017734342f66b615189e7596Daniel Nishi for (Map.Entry<String, PackageStats> entry : filterOnlyPrimaryUser().entrySet()) { 12377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi PackageStats stat = entry.getValue(); 12477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi long appSize = stat.codeSize + stat.dataSize; 12577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi long cacheSize = stat.cacheSize; 12677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi if (isExternal) { 12777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi appSize += stat.externalCodeSize + stat.externalDataSize; 12877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi cacheSize += stat.externalCacheSize; 12977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 13077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi appSizeSum += appSize; 13177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi cacheSizeSum += cacheSize; 13277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 13377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi names.put(stat.packageName); 13477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi appSizeList.put(appSize); 13577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi cacheSizeList.put(cacheSize); 13677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 13777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(PACKAGE_NAMES_KEY, names); 13877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(APP_SIZES_KEY, appSizeList); 13977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(APP_CACHES_KEY, cacheSizeList); 14077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(APP_SIZE_AGG_KEY, appSizeSum); 14177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi json.put(APP_CACHE_AGG_KEY, cacheSizeSum); 14277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 14377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi 14477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi /** 145e47eac74f7c80738017734342f66b615189e7596Daniel Nishi * A given package may exist for multiple users with distinct sizes. This function filters 146e47eac74f7c80738017734342f66b615189e7596Daniel Nishi * the packages that do not belong to user 0 out to ensure that we get good stats for a subset. 14777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi * @return A mapping of package name to merged package stats. 14877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi */ 149e47eac74f7c80738017734342f66b615189e7596Daniel Nishi private ArrayMap<String, PackageStats> filterOnlyPrimaryUser() { 15077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi ArrayMap<String, PackageStats> packageMap = new ArrayMap<>(); 15177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi for (PackageStats stat : mPackageStats) { 152e47eac74f7c80738017734342f66b615189e7596Daniel Nishi if (stat.userHandle != UserHandle.USER_SYSTEM) { 153e47eac74f7c80738017734342f66b615189e7596Daniel Nishi continue; 154e47eac74f7c80738017734342f66b615189e7596Daniel Nishi } 155e47eac74f7c80738017734342f66b615189e7596Daniel Nishi 15677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi PackageStats existingStats = packageMap.get(stat.packageName); 15777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi if (existingStats != null) { 15877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi existingStats.cacheSize += stat.cacheSize; 15977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi existingStats.codeSize += stat.codeSize; 16077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi existingStats.dataSize += stat.dataSize; 16177a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi existingStats.externalCacheSize += stat.externalCacheSize; 16277a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi existingStats.externalCodeSize += stat.externalCodeSize; 16377a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi existingStats.externalDataSize += stat.externalDataSize; 16477a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } else { 16577a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi packageMap.put(stat.packageName, new PackageStats(stat)); 16677a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 16777a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 16877a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi return packageMap; 16977a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi } 17077a78c6f4412dccc58075685ad77f3e41d85f2e4Daniel Nishi}