Use custom logic to check storage space.
`JobInfo.Builder.setRequiresStorageNotLow` doesn't meet out needs
because:
- The background dexopt job will also remove unused dexopt artifacts and
downgrade unused apps, to free up space, so it should run even if the
space is low.
- We need more fine-grained logic because some apps are on internal
storage and some apps are on external storage.
Bug: 255565888
Test: atest ArtServiceTests
Test: -
1. Fill the disk by `fallocate`.
2. adb shell pm art optimize-packages bg-dexopt
3. See compilation skipped for all apps.
Ignore-AOSP-First: ART Services.
Change-Id: I56374fbdd3f96401aa848178891ce9777feb663a
diff --git a/libartservice/service/java/com/android/server/art/DexOptimizer.java b/libartservice/service/java/com/android/server/art/DexOptimizer.java
index 21333a5..c9b5f8d 100644
--- a/libartservice/service/java/com/android/server/art/DexOptimizer.java
+++ b/libartservice/service/java/com/android/server/art/DexOptimizer.java
@@ -32,6 +32,7 @@
import android.os.ServiceSpecificException;
import android.os.SystemProperties;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -49,6 +50,7 @@
import com.google.auto.value.AutoValue;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -151,6 +153,7 @@
long cpuTimeMs = 0;
long sizeBytes = 0;
long sizeBeforeBytes = 0;
+ boolean isSkippedDueToStorageLow = false;
try {
var target = DexoptTarget.<DexInfoType>builder()
.setDexInfo(dexInfo)
@@ -172,6 +175,23 @@
continue;
}
+ try {
+ // `StorageManager.getAllocatableBytes` returns (free space + space used
+ // by clearable cache - low storage threshold). Since we only compare
+ // the result with 0, the clearable cache doesn't make a difference.
+ // When the free space is below the threshold, there should be no
+ // clearable cache left because system cleans up cache every minute.
+ if ((mParams.getFlags() & ArtFlags.FLAG_SKIP_IF_STORAGE_LOW) != 0
+ && mInjector.getStorageManager().getAllocatableBytes(
+ mPkg.getStorageUuid())
+ <= 0) {
+ isSkippedDueToStorageLow = true;
+ continue;
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to check storage. Assuming storage not low", e);
+ }
+
IArtdCancellationSignal artdCancellationSignal =
mInjector.getArtd().createCancellationSignal();
mCancellationSignal.setOnCancelListener(() -> {
@@ -208,7 +228,7 @@
} finally {
results.add(new DexContainerFileOptimizeResult(dexInfo.dexPath(),
abi.isPrimaryAbi(), abi.name(), compilerFilter, status, wallTimeMs,
- cpuTimeMs, sizeBytes, sizeBeforeBytes));
+ cpuTimeMs, sizeBytes, sizeBeforeBytes, isSkippedDueToStorageLow));
if (status != OptimizeResult.OPTIMIZE_SKIPPED
&& status != OptimizeResult.OPTIMIZE_PERFORMED) {
succeeded = false;
@@ -653,5 +673,10 @@
public IArtd getArtd() {
return Utils.getArtd();
}
+
+ @NonNull
+ public StorageManager getStorageManager() {
+ return Objects.requireNonNull(mContext.getSystemService(StorageManager.class));
+ }
}
}