summaryrefslogtreecommitdiff
path: root/apex/blobstore
diff options
context:
space:
mode:
Diffstat (limited to 'apex/blobstore')
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java55
-rw-r--r--apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl1
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java26
3 files changed, 71 insertions, 11 deletions
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index c339351759cd..cb87c6cefef5 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
+import android.os.LimitExceededException;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.RemoteCallback;
@@ -167,6 +168,12 @@ public class BlobStoreManager {
* finalized (either committed or abandoned) within a reasonable period of
* time, typically about a week.
*
+ * <p> If an app is planning to acquire a lease on this data (using
+ * {@link #acquireLease(BlobHandle, int)} or one of it's other variants) after committing
+ * this data (using {@link Session#commit(Executor, Consumer)}), it is recommended that
+ * the app checks the remaining quota for acquiring a lease first using
+ * {@link #getRemainingLeaseQuotaBytes()} and can skip contributing this data if needed.
+ *
* @param blobHandle the {@link BlobHandle} identifier for which a new session
* needs to be created.
* @return positive, non-zero unique id that represents the created session.
@@ -294,8 +301,11 @@ public class BlobStoreManager {
* @throws IllegalArgumentException when {@code blobHandle} is invalid or
* if the {@code leaseExpiryTimeMillis} is greater than the
* {@link BlobHandle#getExpiryTimeMillis()}.
- * @throws IllegalStateException when a lease could not be acquired, such as when the
- * caller is trying to acquire too many leases.
+ * @throws LimitExceededException when a lease could not be acquired, such as when the
+ * caller is trying to acquire leases on too much data. Apps
+ * can avoid this by checking the remaining quota using
+ * {@link #getRemainingLeaseQuotaBytes()} before trying to
+ * acquire a lease.
*
* @see {@link #acquireLease(BlobHandle, int)}
* @see {@link #acquireLease(BlobHandle, CharSequence)}
@@ -307,6 +317,7 @@ public class BlobStoreManager {
mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
+ e.maybeRethrow(LimitExceededException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -350,8 +361,11 @@ public class BlobStoreManager {
* @throws IllegalArgumentException when {@code blobHandle} is invalid or
* if the {@code leaseExpiryTimeMillis} is greater than the
* {@link BlobHandle#getExpiryTimeMillis()}.
- * @throws IllegalStateException when a lease could not be acquired, such as when the
- * caller is trying to acquire too many leases.
+ * @throws LimitExceededException when a lease could not be acquired, such as when the
+ * caller is trying to acquire leases on too much data. Apps
+ * can avoid this by checking the remaining quota using
+ * {@link #getRemainingLeaseQuotaBytes()} before trying to
+ * acquire a lease.
*
* @see {@link #acquireLease(BlobHandle, int, long)}
* @see {@link #acquireLease(BlobHandle, CharSequence)}
@@ -363,6 +377,7 @@ public class BlobStoreManager {
mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
+ e.maybeRethrow(LimitExceededException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -399,8 +414,11 @@ public class BlobStoreManager {
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
* exist or the caller does not have access to it.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
- * @throws IllegalStateException when a lease could not be acquired, such as when the
- * caller is trying to acquire too many leases.
+ * @throws LimitExceededException when a lease could not be acquired, such as when the
+ * caller is trying to acquire leases on too much data. Apps
+ * can avoid this by checking the remaining quota using
+ * {@link #getRemainingLeaseQuotaBytes()} before trying to
+ * acquire a lease.
*
* @see {@link #acquireLease(BlobHandle, int, long)}
* @see {@link #acquireLease(BlobHandle, CharSequence, long)}
@@ -443,8 +461,11 @@ public class BlobStoreManager {
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
* exist or the caller does not have access to it.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
- * @throws IllegalStateException when a lease could not be acquired, such as when the
- * caller is trying to acquire too many leases.
+ * @throws LimitExceededException when a lease could not be acquired, such as when the
+ * caller is trying to acquire leases on too much data. Apps
+ * can avoid this by checking the remaining quota using
+ * {@link #getRemainingLeaseQuotaBytes()} before trying to
+ * acquire a lease.
*
* @see {@link #acquireLease(BlobHandle, int)}
* @see {@link #acquireLease(BlobHandle, CharSequence, long)}
@@ -478,6 +499,24 @@ public class BlobStoreManager {
}
/**
+ * Return the remaining quota size for acquiring a lease (in bytes) which indicates the
+ * remaining amount of data that an app can acquire a lease on before the System starts
+ * rejecting lease requests.
+ *
+ * If an app wants to acquire a lease on a blob but the remaining quota size is not sufficient,
+ * then it can try releasing leases on any older blobs which are not needed anymore.
+ *
+ * @return the remaining quota size for acquiring a lease.
+ */
+ public @IntRange(from = 0) long getRemainingLeaseQuotaBytes() {
+ try {
+ return mService.getRemainingLeaseQuotaBytes(mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Wait until any pending tasks (like persisting data to disk) have finished.
*
* @hide
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index 20c15ab57496..39a9fb4bb1f4 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -31,6 +31,7 @@ interface IBlobStoreManager {
void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description,
long leaseTimeoutMillis, in String packageName);
void releaseLease(in BlobHandle handle, in String packageName);
+ long getRemainingLeaseQuotaBytes(String packageName);
void waitForIdle(in RemoteCallback callback);
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 15c069fbd007..65ccb997343b 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -62,7 +62,9 @@ import android.content.res.Resources;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.LimitExceededException;
import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.SystemClock;
@@ -394,9 +396,9 @@ 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"
+ if (blobMetadata.getSize()
+ > getRemainingLeaseQuotaBytesInternal(callingUid, callingPackage)) {
+ throw new LimitExceededException("Total amount of data with an active lease"
+ " is exceeding the max limit");
}
blobMetadata.addLeasee(callingPackage, callingUid,
@@ -445,6 +447,14 @@ public class BlobStoreManagerService extends SystemService {
}
}
+ private long getRemainingLeaseQuotaBytesInternal(int callingUid, String callingPackage) {
+ synchronized (mBlobsLock) {
+ final long remainingQuota = BlobStoreConfig.getAppDataBytesLimit()
+ - getTotalUsageBytesLocked(callingUid, callingPackage);
+ return remainingQuota > 0 ? remainingQuota : 0;
+ }
+ }
+
private List<BlobInfo> queryBlobsForUserInternal(int userId) {
final ArrayList<BlobInfo> blobInfos = new ArrayList<>();
synchronized (mBlobsLock) {
@@ -1302,6 +1312,8 @@ public class BlobStoreManagerService extends SystemService {
leaseExpiryTimeMillis, callingUid, packageName);
} catch (Resources.NotFoundException e) {
throw new IllegalArgumentException(e);
+ } catch (LimitExceededException e) {
+ throw new ParcelableException(e);
}
}
@@ -1324,6 +1336,14 @@ public class BlobStoreManagerService extends SystemService {
}
@Override
+ public long getRemainingLeaseQuotaBytes(@NonNull String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ verifyCallingPackage(callingUid, packageName);
+
+ return getRemainingLeaseQuotaBytesInternal(callingUid, packageName);
+ }
+
+ @Override
public void waitForIdle(@NonNull RemoteCallback remoteCallback) {
Objects.requireNonNull(remoteCallback, "remoteCallback must not be null");