diff options
author | Sudheer Shanka <sudheersai@google.com> | 2020-03-06 22:39:06 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-03-06 22:39:06 +0000 |
commit | cd629dd39af8fd9fa10b590ce43bd7ff9dba52d3 (patch) | |
tree | 072a90f14b6f0feddb89dcb64354211c24ccbbe0 /apex/blobstore/service/java | |
parent | 6c725002b7c98334ece241d7c33620057c21a1ab (diff) | |
parent | 364364ba411892217764212c4dc242ba1e6a95f0 (diff) |
Merge "Add a limit on how much data an app can acquire a lease on." into rvc-dev
Diffstat (limited to 'apex/blobstore/service/java')
3 files changed, 169 insertions, 15 deletions
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java index 888c3af8bbf7..dab4797b313f 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java @@ -242,7 +242,7 @@ class BlobMetadata { return hasOtherLeasees(null, uid); } - private boolean isALeasee(@Nullable String packageName, int uid) { + boolean isALeasee(@Nullable String packageName, int uid) { synchronized (mMetadataLock) { // Check if the package is a leasee of the data blob. for (int i = 0, size = mLeasees.size(); i < size; ++i) { diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java index 6d31b2c30067..5e8ea7afaec7 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java @@ -15,12 +15,23 @@ */ package com.android.server.blob; +import static android.provider.DeviceConfig.NAMESPACE_BLOBSTORE; +import static android.text.format.Formatter.FLAG_IEC_UNITS; +import static android.text.format.Formatter.formatFileSize; +import static android.util.TimeUtils.formatDuration; + import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.os.Environment; +import android.provider.DeviceConfig; +import android.provider.DeviceConfig.Properties; +import android.util.DataUnit; import android.util.Log; import android.util.Slog; +import com.android.internal.util.IndentingPrintWriter; + import java.io.File; import java.util.concurrent.TimeUnit; @@ -55,6 +66,76 @@ class BlobStoreConfig { */ public static final long SESSION_EXPIRY_TIMEOUT_MILLIS = TimeUnit.DAYS.toMillis(7); + public static class DeviceConfigProperties { + /** + * Denotes how low the limit for the amount of data, that an app will be allowed to acquire + * a lease on, can be. + */ + public static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR = + "total_bytes_per_app_limit_floor"; + public static final long DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR = + DataUnit.MEBIBYTES.toBytes(300); // 300 MiB + public static long TOTAL_BYTES_PER_APP_LIMIT_FLOOR = + DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR; + + /** + * Denotes the maximum amount of data an app can acquire a lease on, in terms of fraction + * of total disk space. + */ + public static final String KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION = + "total_bytes_per_app_limit_fraction"; + public static final float DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION = 0.01f; + public static float TOTAL_BYTES_PER_APP_LIMIT_FRACTION = + DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION; + + static void refresh(Properties properties) { + if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) { + return; + } + properties.getKeyset().forEach(key -> { + switch (key) { + case KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR: + TOTAL_BYTES_PER_APP_LIMIT_FLOOR = properties.getLong(key, + DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR); + break; + case KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION: + TOTAL_BYTES_PER_APP_LIMIT_FRACTION = properties.getFloat(key, + DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION); + break; + default: + Slog.wtf(TAG, "Unknown key in device config properties: " + key); + } + }); + } + + static void dump(IndentingPrintWriter fout, Context context) { + final String dumpFormat = "%s: [cur: %s, def: %s]"; + fout.println(String.format(dumpFormat, KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR, + formatFileSize(context, TOTAL_BYTES_PER_APP_LIMIT_FLOOR, FLAG_IEC_UNITS), + formatFileSize(context, DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR, + FLAG_IEC_UNITS))); + fout.println(String.format(dumpFormat, KEY_TOTAL_BYTES_PER_APP_LIMIT_FRACTION, + TOTAL_BYTES_PER_APP_LIMIT_FRACTION, + DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FRACTION)); + } + } + + public static void initialize(Context context) { + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_BLOBSTORE, + context.getMainExecutor(), + properties -> DeviceConfigProperties.refresh(properties)); + DeviceConfigProperties.refresh(DeviceConfig.getProperties(NAMESPACE_BLOBSTORE)); + } + + /** + * Returns the maximum amount of data that an app can acquire a lease on. + */ + public static long getAppDataBytesLimit() { + final long totalBytesLimit = (long) (Environment.getDataSystemDirectory().getTotalSpace() + * DeviceConfigProperties.TOTAL_BYTES_PER_APP_LIMIT_FRACTION); + return Math.max(DeviceConfigProperties.TOTAL_BYTES_PER_APP_LIMIT_FLOOR, totalBytesLimit); + } + @Nullable public static File prepareBlobFile(long sessionId) { final File blobsDir = prepareBlobsDir(); @@ -123,4 +204,21 @@ class BlobStoreConfig { public static File getBlobStoreRootDir() { return new File(Environment.getDataSystemDirectory(), ROOT_DIR_NAME); } + + public static void dump(IndentingPrintWriter fout, Context context) { + fout.println("XML current version: " + XML_VERSION_CURRENT); + + fout.println("Idle job ID: " + IDLE_JOB_ID); + fout.println("Idle job period: " + formatDuration(IDLE_JOB_PERIOD_MILLIS)); + + fout.println("Session expiry timeout: " + formatDuration(SESSION_EXPIRY_TIMEOUT_MILLIS)); + + fout.println("Total bytes per app limit: " + formatFileSize(context, + getAppDataBytesLimit(), FLAG_IEC_UNITS)); + + fout.println("Device config properties:"); + fout.increaseIndent(); + DeviceConfigProperties.dump(fout, context); + fout.decreaseIndent(); + } } diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java index 3116eb4c2407..7006781e4e4e 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -188,7 +188,9 @@ public class BlobStoreManagerService extends SystemService { @Override public void onBootPhase(int phase) { - if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { + if (phase == PHASE_ACTIVITY_MANAGER_READY) { + BlobStoreConfig.initialize(mContext); + } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { synchronized (mBlobsLock) { final SparseArray<SparseArray<String>> allPackages = getAllPackages(); readBlobSessionsLocked(allPackages); @@ -381,6 +383,11 @@ public class BlobStoreManagerService extends SystemService { throw new IllegalArgumentException( "Lease expiry cannot be later than blobs expiry time"); } + if (getTotalUsageBytesLocked(callingUid, callingPackage) + + blobMetadata.getSize() > BlobStoreConfig.getAppDataBytesLimit()) { + throw new IllegalStateException("Total amount of data with an active lease" + + " is exceeding the max limit"); + } blobMetadata.addLeasee(callingPackage, callingUid, descriptionResId, description, leaseExpiryTimeMillis); if (LOGV) { @@ -391,6 +398,18 @@ public class BlobStoreManagerService extends SystemService { } } + @VisibleForTesting + @GuardedBy("mBlobsLock") + long getTotalUsageBytesLocked(int callingUid, String callingPackage) { + final AtomicLong totalBytes = new AtomicLong(0); + forEachBlobInUser((blobMetadata) -> { + if (blobMetadata.isALeasee(callingPackage, callingUid)) { + totalBytes.getAndAdd(blobMetadata.getSize()); + } + }, UserHandle.getUserId(callingUid)); + return totalBytes.get(); + } + private void releaseLeaseInternal(BlobHandle blobHandle, int callingUid, String callingPackage) { synchronized (mBlobsLock) { @@ -1236,8 +1255,10 @@ public class BlobStoreManagerService extends SystemService { } synchronized (mBlobsLock) { - fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId); - fout.println(); + if (dumpArgs.shouldDumpAllSections()) { + fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId); + fout.println(); + } if (dumpArgs.shouldDumpSessions()) { dumpSessionsLocked(fout, dumpArgs); @@ -1248,6 +1269,14 @@ public class BlobStoreManagerService extends SystemService { fout.println(); } } + + if (dumpArgs.shouldDumpConfig()) { + fout.println("BlobStore config:"); + fout.increaseIndent(); + BlobStoreConfig.dump(fout, mContext); + fout.decreaseIndent(); + fout.println(); + } } @Override @@ -1260,14 +1289,16 @@ public class BlobStoreManagerService extends SystemService { } static final class DumpArgs { + private static final int FLAG_DUMP_SESSIONS = 1 << 0; + private static final int FLAG_DUMP_BLOBS = 1 << 1; + private static final int FLAG_DUMP_CONFIG = 1 << 2; + + private int mSelectedSectionFlags; private boolean mDumpFull; private final ArrayList<String> mDumpPackages = new ArrayList<>(); private final ArrayList<Integer> mDumpUids = new ArrayList<>(); private final ArrayList<Integer> mDumpUserIds = new ArrayList<>(); private final ArrayList<Long> mDumpBlobIds = new ArrayList<>(); - private boolean mDumpOnlySelectedSections; - private boolean mDumpSessions; - private boolean mDumpBlobs; private boolean mDumpHelp; public boolean shouldDumpSession(String packageName, int uid, long blobId) { @@ -1286,18 +1317,41 @@ public class BlobStoreManagerService extends SystemService { return true; } + public boolean shouldDumpAllSections() { + return mSelectedSectionFlags == 0; + } + + public void allowDumpSessions() { + mSelectedSectionFlags |= FLAG_DUMP_SESSIONS; + } + public boolean shouldDumpSessions() { - if (!mDumpOnlySelectedSections) { + if (shouldDumpAllSections()) { return true; } - return mDumpSessions; + return (mSelectedSectionFlags & FLAG_DUMP_SESSIONS) != 0; + } + + public void allowDumpBlobs() { + mSelectedSectionFlags |= FLAG_DUMP_BLOBS; } public boolean shouldDumpBlobs() { - if (!mDumpOnlySelectedSections) { + if (shouldDumpAllSections()) { + return true; + } + return (mSelectedSectionFlags & FLAG_DUMP_BLOBS) != 0; + } + + public void allowDumpConfig() { + mSelectedSectionFlags |= FLAG_DUMP_CONFIG; + } + + public boolean shouldDumpConfig() { + if (shouldDumpAllSections()) { return true; } - return mDumpBlobs; + return (mSelectedSectionFlags & FLAG_DUMP_CONFIG) != 0; } public boolean shouldDumpBlob(long blobId) { @@ -1334,11 +1388,11 @@ public class BlobStoreManagerService extends SystemService { dumpArgs.mDumpFull = true; } } else if ("--sessions".equals(opt)) { - dumpArgs.mDumpOnlySelectedSections = true; - dumpArgs.mDumpSessions = true; + dumpArgs.allowDumpSessions(); } else if ("--blobs".equals(opt)) { - dumpArgs.mDumpOnlySelectedSections = true; - dumpArgs.mDumpBlobs = true; + dumpArgs.allowDumpBlobs(); + } else if ("--config".equals(opt)) { + dumpArgs.allowDumpConfig(); } else if ("--package".equals(opt) || "-p".equals(opt)) { dumpArgs.mDumpPackages.add(getStringArgRequired(args, ++i, "packageName")); } else if ("--uid".equals(opt) || "-u".equals(opt)) { @@ -1397,6 +1451,8 @@ public class BlobStoreManagerService extends SystemService { printWithIndent(pw, "Dump only the sessions info"); pw.println("--blobs"); printWithIndent(pw, "Dump only the committed blobs info"); + pw.println("--config"); + printWithIndent(pw, "Dump only the config values"); pw.println("--package | -p [package-name]"); printWithIndent(pw, "Dump blobs info associated with the given package"); pw.println("--uid | -u [uid]"); |