diff --git a/core/proto/android/service/diskstats.proto b/core/proto/android/service/diskstats.proto new file mode 100644 index 0000000000000..4d865264d19e2 --- /dev/null +++ b/core/proto/android/service/diskstats.proto @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; + +package android.service.diskstats; + +option java_multiple_files = true; +option java_outer_classname = "DiskStatsServiceProto"; + +message DiskStatsServiceDumpProto { + enum EncryptionType { + // Unknown encryption type + ENCRYPTION_UNKNOWN = 0; + // No encryption + ENCRYPTION_NONE = 1; + // Full disk encryption + ENCRYPTION_FULL_DISK = 2; + // File-based encryption + ENCRYPTION_FILE_BASED = 3; + } + // Whether the latency test resulted in an error + bool has_test_error = 1; + // If the test errored, error message is contained here + string error_message = 2; + // 512B write latency in milliseconds, if the test was successful + int32 write_512b_latency_millis = 3; + // Free Space in the major partitions + repeated DiskStatsFreeSpaceProto partitions_free_space = 4; + // Is the device using file-based encryption, full disk encryption or other + EncryptionType encryption = 5; + // Cached values of folder sizes, etc. + DiskStatsCachedValuesProto cached_folder_sizes = 6; +} + +message DiskStatsCachedValuesProto { + // Total app data size, in kilobytes + int64 agg_apps_size = 1; + // Total app cache size, in kilobytes + int64 agg_apps_cache_size = 2; + // Size of image files, in kilobytes + int64 photos_size = 3; + // Size of video files, in kilobytes + int64 videos_size = 4; + // Size of audio files, in kilobytes + int64 audio_size = 5; + // Size of downloads, in kilobytes + int64 downloads_size = 6; + // Size of system directory, in kilobytes + int64 system_size = 7; + // Size of other files, in kilobytes + int64 other_size = 8; + // Sizes of individual packages + repeated DiskStatsAppSizesProto app_sizes = 9; +} + +message DiskStatsAppSizesProto { + // Name of the package + string package_name = 1; + // App's data size in kilobytes + int64 app_size = 2; + // App's cache size in kilobytes + int64 cache_size = 3; +} + +message DiskStatsFreeSpaceProto { + enum Folder { + // Data folder + FOLDER_DATA = 0; + // Cache folder + FOLDER_CACHE = 1; + // System folder + FOLDER_SYSTEM = 2; + } + // Which folder? + Folder folder = 1; + // Available space, in kilobytes + int64 available_space = 2; + // Total space, in kilobytes + int64 total_space = 3; +} diff --git a/services/core/java/com/android/server/DiskStatsService.java b/services/core/java/com/android/server/DiskStatsService.java index 962ac6fc540f8..1bdff6be4bbec 100644 --- a/services/core/java/com/android/server/DiskStatsService.java +++ b/services/core/java/com/android/server/DiskStatsService.java @@ -22,13 +22,20 @@ import android.os.Environment; import android.os.StatFs; import android.os.SystemClock; import android.os.storage.StorageManager; +import android.service.diskstats.DiskStatsAppSizesProto; +import android.service.diskstats.DiskStatsCachedValuesProto; +import android.service.diskstats.DiskStatsFreeSpaceProto; +import android.service.diskstats.DiskStatsServiceDumpProto; import android.util.Log; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.server.storage.DiskStatsFileLogger; import com.android.server.storage.DiskStatsLoggingService; import libcore.io.IoUtils; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -78,30 +85,68 @@ public class DiskStatsService extends Binder { long after = SystemClock.uptimeMillis(); if (tmp.exists()) tmp.delete(); - if (error != null) { - pw.print("Test-Error: "); - pw.println(error.toString()); + boolean protoFormat = hasOption(args, "--proto"); + ProtoOutputStream proto = null; + + if (protoFormat) { + proto = new ProtoOutputStream(fd); + pw = null; + proto.write(DiskStatsServiceDumpProto.HAS_TEST_ERROR, error != null); + if (error != null) { + proto.write(DiskStatsServiceDumpProto.ERROR_MESSAGE, error.toString()); + } else { + proto.write(DiskStatsServiceDumpProto.WRITE_512B_LATENCY_MILLIS, after - before); + } } else { - pw.print("Latency: "); - pw.print(after - before); - pw.println("ms [512B Data Write]"); + if (error != null) { + pw.print("Test-Error: "); + pw.println(error.toString()); + } else { + pw.print("Latency: "); + pw.print(after - before); + pw.println("ms [512B Data Write]"); + } } - reportFreeSpace(Environment.getDataDirectory(), "Data", pw); - reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw); - reportFreeSpace(new File("/system"), "System", pw); + reportFreeSpace(Environment.getDataDirectory(), "Data", pw, proto, + DiskStatsFreeSpaceProto.FOLDER_DATA); + reportFreeSpace(Environment.getDownloadCacheDirectory(), "Cache", pw, proto, + DiskStatsFreeSpaceProto.FOLDER_CACHE); + reportFreeSpace(new File("/system"), "System", pw, proto, + DiskStatsFreeSpaceProto.FOLDER_SYSTEM); - if (StorageManager.isFileEncryptedNativeOnly()) { + boolean fileBased = StorageManager.isFileEncryptedNativeOnly(); + boolean blockBased = fileBased ? false : StorageManager.isBlockEncrypted(); + if (protoFormat) { + if (fileBased) { + proto.write(DiskStatsServiceDumpProto.ENCRYPTION, + DiskStatsServiceDumpProto.ENCRYPTION_FILE_BASED); + } else if (blockBased) { + proto.write(DiskStatsServiceDumpProto.ENCRYPTION, + DiskStatsServiceDumpProto.ENCRYPTION_FULL_DISK); + } else { + proto.write(DiskStatsServiceDumpProto.ENCRYPTION, + DiskStatsServiceDumpProto.ENCRYPTION_NONE); + } + } else if (fileBased) { pw.println("File-based Encryption: true"); } - reportCachedValues(pw); + if (protoFormat) { + reportCachedValuesProto(proto); + } else { + reportCachedValues(pw); + } + if (protoFormat) { + proto.flush(); + } // TODO: Read /proc/yaffs and report interesting values; // add configurable (through args) performance test parameters. } - private void reportFreeSpace(File path, String name, PrintWriter pw) { + private void reportFreeSpace(File path, String name, PrintWriter pw, + ProtoOutputStream proto, int folderType) { try { StatFs statfs = new StatFs(path.getPath()); long bsize = statfs.getBlockSize(); @@ -112,22 +157,44 @@ public class DiskStatsService extends Binder { "Invalid stat: bsize=" + bsize + " avail=" + avail + " total=" + total); } - pw.print(name); - pw.print("-Free: "); - pw.print(avail * bsize / 1024); - pw.print("K / "); - pw.print(total * bsize / 1024); - pw.print("K total = "); - pw.print(avail * 100 / total); - pw.println("% free"); + if (proto != null) { + long freeSpaceToken = proto.start(DiskStatsServiceDumpProto.PARTITIONS_FREE_SPACE); + proto.write(DiskStatsFreeSpaceProto.FOLDER, folderType); + proto.write(DiskStatsFreeSpaceProto.AVAILABLE_SPACE, avail * bsize / 1024); + proto.write(DiskStatsFreeSpaceProto.TOTAL_SPACE, total * bsize / 1024); + proto.end(freeSpaceToken); + } else { + pw.print(name); + pw.print("-Free: "); + pw.print(avail * bsize / 1024); + pw.print("K / "); + pw.print(total * bsize / 1024); + pw.print("K total = "); + pw.print(avail * 100 / total); + pw.println("% free"); + } } catch (IllegalArgumentException e) { - pw.print(name); - pw.print("-Error: "); - pw.println(e.toString()); + if (proto != null) { + // Empty proto + } else { + pw.print(name); + pw.print("-Error: "); + pw.println(e.toString()); + } return; } } + private boolean hasOption(String[] args, String arg) { + for (String opt : args) { + if (arg.equals(opt)) { + return true; + } + } + return false; + } + + // If you change this method, make sure to modify the Proto version of this method as well. private void reportCachedValues(PrintWriter pw) { try { String jsonString = IoUtils.readFileAsString(DISKSTATS_DUMP_FILE); @@ -159,4 +226,52 @@ public class DiskStatsService extends Binder { } } + private void reportCachedValuesProto(ProtoOutputStream proto) { + try { + String jsonString = IoUtils.readFileAsString(DISKSTATS_DUMP_FILE); + JSONObject json = new JSONObject(jsonString); + long cachedValuesToken = proto.start(DiskStatsServiceDumpProto.CACHED_FOLDER_SIZES); + + proto.write(DiskStatsCachedValuesProto.AGG_APPS_SIZE, + json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY)); + proto.write(DiskStatsCachedValuesProto.AGG_APPS_CACHE_SIZE, + json.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY)); + proto.write(DiskStatsCachedValuesProto.PHOTOS_SIZE, + json.getLong(DiskStatsFileLogger.PHOTOS_KEY)); + proto.write(DiskStatsCachedValuesProto.VIDEOS_SIZE, + json.getLong(DiskStatsFileLogger.VIDEOS_KEY)); + proto.write(DiskStatsCachedValuesProto.AUDIO_SIZE, + json.getLong(DiskStatsFileLogger.AUDIO_KEY)); + proto.write(DiskStatsCachedValuesProto.DOWNLOADS_SIZE, + json.getLong(DiskStatsFileLogger.DOWNLOADS_KEY)); + proto.write(DiskStatsCachedValuesProto.SYSTEM_SIZE, + json.getLong(DiskStatsFileLogger.SYSTEM_KEY)); + proto.write(DiskStatsCachedValuesProto.OTHER_SIZE, + json.getLong(DiskStatsFileLogger.MISC_KEY)); + + JSONArray packageNamesArray = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY); + JSONArray appSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY); + JSONArray cacheSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY); + final int len = packageNamesArray.length(); + if (len == appSizesArray.length() && len == cacheSizesArray.length()) { + for (int i = 0; i < len; i++) { + long packageToken = proto.start(DiskStatsCachedValuesProto.APP_SIZES); + + proto.write(DiskStatsAppSizesProto.PACKAGE_NAME, + packageNamesArray.getString(i)); + proto.write(DiskStatsAppSizesProto.APP_SIZE, appSizesArray.getLong(i)); + proto.write(DiskStatsAppSizesProto.CACHE_SIZE, cacheSizesArray.getLong(i)); + + proto.end(packageToken); + } + } else { + Slog.wtf(TAG, "Sizes of packageNamesArray, appSizesArray and cacheSizesArray " + + "are not the same"); + } + + proto.end(cachedValuesToken); + } catch (IOException | JSONException e) { + Log.w(TAG, "exception reading diskstats cache file", e); + } + } }