Fetch today's I/O usage stats collected during previous boot.

Watchdog daemon fetches today's I/O usage stat, that were collected
during the previous boot, from CarWatchdogService, so the daemon can the
have the aggregated view of the total bytes written to disk since the
beginning of the day.

Test: atest CarWatchdogServiceUnitTest WatchdogStorageUnitTest
libwatchdog_test
Bug: 192665981

Change-Id: Ieb3f3ba27d4d94f68f94788a364007fbc022a872
Merged-In: Ieb3f3ba27d4d94f68f94788a364007fbc022a872
(cherry picked from commit 766a91829ffdb341a6d59ed65e7e0d2f38273357)
diff --git a/service/src/com/android/car/watchdog/CarWatchdogService.java b/service/src/com/android/car/watchdog/CarWatchdogService.java
index 360a559..6353958 100644
--- a/service/src/com/android/car/watchdog/CarWatchdogService.java
+++ b/service/src/com/android/car/watchdog/CarWatchdogService.java
@@ -28,6 +28,7 @@
 import android.automotive.watchdog.internal.PackageIoOveruseStats;
 import android.automotive.watchdog.internal.PowerCycle;
 import android.automotive.watchdog.internal.StateType;
+import android.automotive.watchdog.internal.UserPackageIoUsageStats;
 import android.automotive.watchdog.internal.UserState;
 import android.car.Car;
 import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
@@ -626,5 +627,15 @@
             }
             service.mWatchdogPerfHandler.resetResourceOveruseStats(new ArraySet<>(packageNames));
         }
+
+        @Override
+        public List<UserPackageIoUsageStats> getTodayIoUsageStats() {
+            CarWatchdogService service = mService.get();
+            if (service == null) {
+                Slogf.w(TAG, "CarWatchdogService is not available");
+                return null;
+            }
+            return service.mWatchdogPerfHandler.getTodayIoUsageStats();
+        }
     }
 }
diff --git a/service/src/com/android/car/watchdog/WatchdogPerfHandler.java b/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
index 0ab0a2b..08f7265 100644
--- a/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
+++ b/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
@@ -45,12 +45,14 @@
 import android.automotive.watchdog.ResourceType;
 import android.automotive.watchdog.internal.ApplicationCategoryType;
 import android.automotive.watchdog.internal.ComponentType;
+import android.automotive.watchdog.internal.IoUsageStats;
 import android.automotive.watchdog.internal.PackageIdentifier;
 import android.automotive.watchdog.internal.PackageIoOveruseStats;
 import android.automotive.watchdog.internal.PackageMetadata;
 import android.automotive.watchdog.internal.PackageResourceOveruseAction;
 import android.automotive.watchdog.internal.PerStateIoOveruseThreshold;
 import android.automotive.watchdog.internal.ResourceSpecificConfiguration;
+import android.automotive.watchdog.internal.UserPackageIoUsageStats;
 import android.car.drivingstate.CarUxRestrictions;
 import android.car.drivingstate.ICarUxRestrictionsChangeListener;
 import android.car.watchdog.CarWatchdogManager;
@@ -733,6 +735,26 @@
         }
     }
 
+    /** Returns today's I/O usage stats for all packages collected during the previous boot. */
+    public List<UserPackageIoUsageStats> getTodayIoUsageStats() {
+        List<UserPackageIoUsageStats> userPackageIoUsageStats = new ArrayList<>();
+        List<WatchdogStorage.IoUsageStatsEntry> entries = mWatchdogStorage.getTodayIoUsageStats();
+        for (int i = 0; i < entries.size(); ++i) {
+            WatchdogStorage.IoUsageStatsEntry entry = entries.get(i);
+            UserPackageIoUsageStats stats = new UserPackageIoUsageStats();
+            stats.userId = entry.userId;
+            stats.packageName = entry.packageName;
+            stats.ioUsageStats = new IoUsageStats();
+            android.automotive.watchdog.IoOveruseStats internalIoUsage =
+                    entry.ioUsage.getInternalIoOveruseStats();
+            stats.ioUsageStats.writtenBytes = internalIoUsage.writtenBytes;
+            stats.ioUsageStats.forgivenWriteBytes = entry.ioUsage.getForgivenWriteBytes();
+            stats.ioUsageStats.totalOveruses = internalIoUsage.totalOveruses;
+            userPackageIoUsageStats.add(stats);
+        }
+        return userPackageIoUsageStats;
+    }
+
     /** Deletes all data for specific user. */
     public void deleteUser(@UserIdInt int userId) {
         synchronized (mLock) {
diff --git a/service/src/com/android/car/watchdog/WatchdogStorage.java b/service/src/com/android/car/watchdog/WatchdogStorage.java
index 42f6313..5b0ebb5 100644
--- a/service/src/com/android/car/watchdog/WatchdogStorage.java
+++ b/service/src/com/android/car/watchdog/WatchdogStorage.java
@@ -36,6 +36,7 @@
 import android.util.Slog;
 
 import com.android.car.CarLog;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.utils.Slogf;
 
@@ -67,6 +68,11 @@
     private final ArrayMap<String, UserPackage> mUserPackagesByKey = new ArrayMap<>();
     private final ArrayMap<String, UserPackage> mUserPackagesById = new ArrayMap<>();
     private TimeSourceInterface mTimeSource = SYSTEM_INSTANCE;
+    private final Object mLock = new Object();
+    // Cache of today's I/O overuse stats collected during the previous boot. The data contained in
+    // the cache won't change until the next boot, so it is safe to cache the data in memory.
+    @GuardedBy("mLock")
+    private final List<IoUsageStatsEntry> mTodayIoUsageStatsEntries = new ArrayList<>();
 
     public WatchdogStorage(Context context) {
         this(context, /* useDataSystemCarDir= */ true);
@@ -136,27 +142,33 @@
 
     /** Returns the saved I/O usage stats for the current day. */
     public List<IoUsageStatsEntry> getTodayIoUsageStats() {
-        ZonedDateTime statsDate =
-                mTimeSource.now().atZone(ZONE_OFFSET).truncatedTo(STATS_TEMPORAL_UNIT);
-        long startEpochSeconds = statsDate.toEpochSecond();
-        long endEpochSeconds = mTimeSource.now().atZone(ZONE_OFFSET).toEpochSecond();
-        ArrayMap<String, WatchdogPerfHandler.PackageIoUsage> ioUsagesById;
-        try (SQLiteDatabase db = mDbHelper.getReadableDatabase()) {
-            ioUsagesById = IoUsageStatsTable.queryStats(db, startEpochSeconds, endEpochSeconds);
-        }
-        List<IoUsageStatsEntry> entries = new ArrayList<>();
-        for (int i = 0; i < ioUsagesById.size(); ++i) {
-            String id = ioUsagesById.keyAt(i);
-            UserPackage userPackage = mUserPackagesById.get(id);
-            if (userPackage == null) {
-                Slogf.i(TAG, "Failed to find user id and package name for unique database id: '%s'",
-                        id);
-                continue;
+        synchronized (mLock) {
+            if (!mTodayIoUsageStatsEntries.isEmpty()) {
+                return new ArrayList<>(mTodayIoUsageStatsEntries);
             }
-            entries.add(new IoUsageStatsEntry(userPackage.getUserId(), userPackage.getPackageName(),
-                    ioUsagesById.valueAt(i)));
+            ZonedDateTime statsDate =
+                    mTimeSource.now().atZone(ZONE_OFFSET).truncatedTo(STATS_TEMPORAL_UNIT);
+            long startEpochSeconds = statsDate.toEpochSecond();
+            long endEpochSeconds = mTimeSource.now().atZone(ZONE_OFFSET).toEpochSecond();
+            ArrayMap<String, WatchdogPerfHandler.PackageIoUsage> ioUsagesById;
+            try (SQLiteDatabase db = mDbHelper.getReadableDatabase()) {
+                ioUsagesById = IoUsageStatsTable.queryStats(db, startEpochSeconds, endEpochSeconds);
+            }
+            for (int i = 0; i < ioUsagesById.size(); ++i) {
+                String id = ioUsagesById.keyAt(i);
+                UserPackage userPackage = mUserPackagesById.get(id);
+                if (userPackage == null) {
+                    Slogf.i(TAG,
+                            "Failed to find user id and package name for unique database id: '%s'",
+                            id);
+                    continue;
+                }
+                mTodayIoUsageStatsEntries.add(new IoUsageStatsEntry(
+                        userPackage.getUserId(), userPackage.getPackageName(),
+                        ioUsagesById.valueAt(i)));
+            }
+            return new ArrayList<>(mTodayIoUsageStatsEntries);
         }
-        return entries;
     }
 
     /** Deletes user package settings and resource overuse stats. */