content: /*
   * Copyright (C) 2018 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.
   */
  
  package com.android.internal.os;
  
  import android.os.StrictMode;
  import android.text.TextUtils;
  import android.util.Slog;
  
  import com.android.internal.annotations.VisibleForTesting;
  
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.IOException;
  import java.nio.file.Files;
  
  
  /**
   * Reads /proc/uid_io/stats which has the line format:
   *
   * uid: foreground_read_chars foreground_write_chars foreground_read_bytes foreground_write_bytes
   * background_read_chars background_write_chars background_read_bytes background_write_bytes
   * foreground_fsync background_fsync
   *
   * This provides the number of bytes/chars read/written in foreground/background for each uid.
   * The file contains a monotonically increasing count of bytes/chars for a single boot.
   */
  public class StoragedUidIoStatsReader {
  
      private static final String TAG = StoragedUidIoStatsReader.class.getSimpleName();
      private static String sUidIoFile = "/proc/uid_io/stats";
  
      public StoragedUidIoStatsReader() {
      }
  
      @VisibleForTesting
      public StoragedUidIoStatsReader(String file) {
          sUidIoFile = file;
      }
  
      /**
       * Notifies when new data is available.
       */
      public interface Callback {
  
          /**
           * Provides data to the client.
           *
           * Note: Bytes are I/O events from a storage device. Chars are data requested by syscalls,
           *   and can be satisfied by caching.
           */
          void onUidStorageStats(int uid, long fgCharsRead, long fgCharsWrite, long fgBytesRead,
                  long fgBytesWrite, long bgCharsRead, long bgCharsWrite, long bgBytesRead,
                  long bgBytesWrite, long fgFsync, long bgFsync);
      }
  
      /**
       * Reads the proc file, calling into the callback with raw absolute value of I/O stats
       * for each UID.
       *
       * @param callback The callback to invoke for each line of the proc file.
       */
      public void readAbsolute(Callback callback) {
          final int oldMask = StrictMode.allowThreadDiskReadsMask();
          File file = new File(sUidIoFile);
          try (BufferedReader reader = Files.newBufferedReader(file.toPath())) {
              String line;
              while ((line = reader.readLine()) != null) {
                  String[] fields = TextUtils.split(line, " ");
                  if (fields.length != 11) {
                      Slog.e(TAG, "Malformed entry in " + sUidIoFile + ": " + line);
                      continue;
                  }
                  try {
                      final String uidStr = fields[0];
                      final int uid = Integer.parseInt(fields[0], 10);
                      final long fgCharsRead = Long.parseLong(fields[1], 10);
                      final long fgCharsWrite = Long.parseLong(fields[2], 10);
                      final long fgBytesRead = Long.parseLong(fields[3], 10);
                      final long fgBytesWrite = Long.parseLong(fields[4], 10);
                      final long bgCharsRead = Long.parseLong(fields[5], 10);
                      final long bgCharsWrite = Long.parseLong(fields[6], 10);
                      final long bgBytesRead = Long.parseLong(fields[7], 10);
                      final long bgBytesWrite = Long.parseLong(fields[8], 10);
                      final long fgFsync = Long.parseLong(fields[9], 10);
                      final long bgFsync = Long.parseLong(fields[10], 10);
                      callback.onUidStorageStats(uid, fgCharsRead, fgCharsWrite, fgBytesRead,
                              fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
                              fgFsync, bgFsync);
                  } catch (NumberFormatException e) {
                      Slog.e(TAG, "Could not parse entry in " + sUidIoFile + ": " + e.getMessage());
                  }
              }
          } catch (IOException e) {
              Slog.e(TAG, "Failed to read " + sUidIoFile + ": " + e.getMessage());
          } finally {
              StrictMode.setThreadPolicyMask(oldMask);
          }
      }
  }
  