Consistent "low storage" behavior.

When freeing cached data, the caller can now provide a "reserved"
size which we won't clear cached data from.

Bug: 38008706
Test: cts-tradefed run commandAndExit cts-dev -m CtsJobSchedulerTestCases -t android.jobscheduler.cts.StorageConstraintTest
Test: cts-tradefed run commandAndExit cts-dev -m CtsAppSecurityHostTestCases -t android.appsecurity.cts.StorageHostTest
Change-Id: Ieb91b3e5345a950d4785fd7915f520f0a68a567a
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 509dd92..dec23d3 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -915,15 +915,8 @@
     return res;
 }
 
-/* Try to ensure free_size bytes of storage are available.
- * Returns 0 on success.
- * This is rather simple-minded because doing a full LRU would
- * be potentially memory-intensive, and without atime it would
- * also require that apps constantly modify file metadata even
- * when just reading from the cache, which is pretty awful.
- */
 binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid,
-        int64_t freeStorageSize, int32_t flags) {
+        int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     std::lock_guard<std::recursive_mutex> lock(mLock);
@@ -938,11 +931,12 @@
         return error("Failed to determine free space for " + data_path);
     }
 
-    int64_t needed = freeStorageSize - free;
+    int64_t cleared = 0;
+    int64_t needed = targetFreeBytes - free;
     LOG(DEBUG) << "Device " << data_path << " has " << free << " free; requested "
-            << freeStorageSize << "; needed " << needed;
+            << targetFreeBytes << "; needed " << needed;
 
-    if (free >= freeStorageSize) {
+    if (free >= targetFreeBytes) {
         return ok();
     }
 
@@ -999,6 +993,7 @@
 
         // 2. Populate tracker stats and insert into priority queue
         ATRACE_BEGIN("populate");
+        int64_t cacheTotal = 0;
         auto cmp = [](std::shared_ptr<CacheTracker> left, std::shared_ptr<CacheTracker> right) {
             return (left->getCacheRatio() < right->getCacheRatio());
         };
@@ -1007,6 +1002,7 @@
         for (const auto& it : trackers) {
             it.second->loadStats();
             queue.push(it.second);
+            cacheTotal += it.second->cacheUsed;
         }
         ATRACE_END();
 
@@ -1023,6 +1019,12 @@
                 break;
             }
 
+            // Only keep clearing when we haven't pushed into reserved area
+            if (cacheReservedBytes > 0 && cleared >= (cacheTotal - cacheReservedBytes)) {
+                LOG(DEBUG) << "Refusing to clear cached data in reserved space";
+                break;
+            }
+
             // Find the best tracker to work with; this might involve swapping
             // if the active tracker is no longer the most over quota
             bool nextBetter = active && !queue.empty()
@@ -1052,13 +1054,14 @@
                 }
                 active->cacheUsed -= item->size;
                 needed -= item->size;
+                cleared += item->size;
             }
 
             // Verify that we're actually done before bailing, since sneaky
             // apps might be using hardlinks
             if (needed <= 0) {
                 free = data_disk_free(data_path);
-                needed = freeStorageSize - free;
+                needed = targetFreeBytes - free;
                 if (needed <= 0) {
                     break;
                 } else {
@@ -1073,11 +1076,11 @@
     }
 
     free = data_disk_free(data_path);
-    if (free >= freeStorageSize) {
+    if (free >= targetFreeBytes) {
         return ok();
     } else {
         return error(StringPrintf("Failed to free up %" PRId64 " on %s; final free space %" PRId64,
-                freeStorageSize, data_path.c_str(), free));
+                targetFreeBytes, data_path.c_str(), free));
     }
 }
 
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 200fc77..7f7fa74 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -99,8 +99,8 @@
     binder::Status removeIdmap(const std::string& overlayApkPath);
     binder::Status rmPackageDir(const std::string& packageDir);
     binder::Status markBootComplete(const std::string& instructionSet);
-    binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t freeStorageSize,
-            int32_t flags);
+    binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
+            int64_t cacheReservedBytes, int32_t flags);
     binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
             const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
     binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 6b99c1d..6185ecb 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -64,7 +64,8 @@
     void removeIdmap(@utf8InCpp String overlayApkPath);
     void rmPackageDir(@utf8InCpp String packageDir);
     void markBootComplete(@utf8InCpp String instructionSet);
-    void freeCache(@nullable @utf8InCpp String uuid, long freeStorageSize, int flags);
+    void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
+            long cacheReservedBytes, int flags);
     void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
             @utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId);
     void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet);
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 174ce77..aed068c 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -146,7 +146,7 @@
     EXPECT_EQ(0, exists("com.example/cache/foo/one"));
     EXPECT_EQ(0, exists("com.example/cache/foo/two"));
 
-    service->freeCache(testUuid, kTbInBytes,
+    service->freeCache(testUuid, kTbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(0, exists("com.example/normal"));
@@ -163,13 +163,13 @@
     touch("com.example/cache/foo/one", kMbInBytes, 60);
     touch("com.example/cache/foo/two", kMbInBytes, 120);
 
-    service->freeCache(testUuid, free() + kKbInBytes,
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
     EXPECT_EQ(0, exists("com.example/cache/foo/two"));
 
-    service->freeCache(testUuid, free() + kKbInBytes,
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/one"));
@@ -197,7 +197,7 @@
     EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar1"));
     EXPECT_EQ(2 * kMbInBytes, size("com.example/cache/bar/bar2"));
 
-    service->freeCache(testUuid, kTbInBytes,
+    service->freeCache(testUuid, kTbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
@@ -219,7 +219,7 @@
 
     setxattr("com.example/cache/foo", "user.cache_group");
 
-    service->freeCache(testUuid, free() + kKbInBytes,
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, exists("com.example/cache/foo/foo1"));
@@ -264,7 +264,7 @@
     setxattr("com.example/cache/tomb", "user.cache_tombstone");
     setxattr("com.example/cache/tomb/group", "user.cache_group");
 
-    service->freeCache(testUuid, free() + kKbInBytes,
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(kMbInBytes, size("com.example/cache/group/file1"));
@@ -285,7 +285,7 @@
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
 
-    service->freeCache(testUuid, free() + kKbInBytes,
+    service->freeCache(testUuid, free() + kKbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, size("com.example/cache/group/file1"));
@@ -306,7 +306,7 @@
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file1"));
     EXPECT_EQ(0, size("com.example/cache/tomb/group/dir/file2"));
 
-    service->freeCache(testUuid, kTbInBytes,
+    service->freeCache(testUuid, kTbInBytes, 0,
             FLAG_FREE_CACHE_V2 | FLAG_FREE_CACHE_V2_DEFY_QUOTA);
 
     EXPECT_EQ(-1, size("com.example/cache/group/file1"));