Merge "Add a limit on no. of active sessions, committed/leased blobs." into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
c9f2fdc0ba
@@ -184,9 +184,8 @@ public class BlobStoreManager {
|
||||
* @throws SecurityException when the caller is not allowed to create a session, such
|
||||
* as when called from an Instant app.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
|
||||
* @throws IllegalStateException when a new session could not be created, such as when the
|
||||
* caller is trying to create too many sessions or when the
|
||||
* device is running low on space.
|
||||
* @throws LimitExceededException when a new session could not be created, such as when the
|
||||
* caller is trying to create too many sessions.
|
||||
*/
|
||||
public @IntRange(from = 1) long createSession(@NonNull BlobHandle blobHandle)
|
||||
throws IOException {
|
||||
@@ -194,6 +193,7 @@ public class BlobStoreManager {
|
||||
return mService.createSession(blobHandle, mContext.getOpPackageName());
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
e.maybeRethrow(LimitExceededException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
@@ -302,8 +302,9 @@ public class BlobStoreManager {
|
||||
* if the {@code leaseExpiryTimeMillis} is greater than the
|
||||
* {@link BlobHandle#getExpiryTimeMillis()}.
|
||||
* @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
|
||||
* caller is trying to acquire too many leases or acquire
|
||||
* leases on too much data. Apps can avoid this by checking
|
||||
* the remaining quota using
|
||||
* {@link #getRemainingLeaseQuotaBytes()} before trying to
|
||||
* acquire a lease.
|
||||
*
|
||||
@@ -362,8 +363,9 @@ public class BlobStoreManager {
|
||||
* if the {@code leaseExpiryTimeMillis} is greater than the
|
||||
* {@link BlobHandle#getExpiryTimeMillis()}.
|
||||
* @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
|
||||
* caller is trying to acquire too many leases or acquire
|
||||
* leases on too much data. Apps can avoid this by checking
|
||||
* the remaining quota using
|
||||
* {@link #getRemainingLeaseQuotaBytes()} before trying to
|
||||
* acquire a lease.
|
||||
*
|
||||
@@ -415,8 +417,9 @@ public class BlobStoreManager {
|
||||
* exist or the caller does not have access to it.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
|
||||
* @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
|
||||
* caller is trying to acquire too many leases or acquire
|
||||
* leases on too much data. Apps can avoid this by checking
|
||||
* the remaining quota using
|
||||
* {@link #getRemainingLeaseQuotaBytes()} before trying to
|
||||
* acquire a lease.
|
||||
*
|
||||
@@ -462,8 +465,9 @@ public class BlobStoreManager {
|
||||
* exist or the caller does not have access to it.
|
||||
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
|
||||
* @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
|
||||
* caller is trying to acquire too many leases or acquire
|
||||
* leases on too much data. Apps can avoid this by checking
|
||||
* the remaining quota using
|
||||
* {@link #getRemainingLeaseQuotaBytes()} before trying to
|
||||
* acquire a lease.
|
||||
*
|
||||
@@ -757,6 +761,8 @@ public class BlobStoreManager {
|
||||
* @throws SecurityException when the caller is not the owner of the session.
|
||||
* @throws IllegalStateException when the caller tries to change access for a blob which is
|
||||
* already committed.
|
||||
* @throws LimitExceededException when the caller tries to explicitly allow too
|
||||
* many packages using this API.
|
||||
*/
|
||||
public void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate)
|
||||
throws IOException {
|
||||
@@ -764,6 +770,7 @@ public class BlobStoreManager {
|
||||
mSession.allowPackageAccess(packageName, certificate);
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(IOException.class);
|
||||
e.maybeRethrow(LimitExceededException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
|
||||
@@ -141,6 +141,36 @@ class BlobStoreConfig {
|
||||
public static long DELETE_ON_LAST_LEASE_DELAY_MS =
|
||||
DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS;
|
||||
|
||||
/**
|
||||
* Denotes the maximum number of active sessions per app at any time.
|
||||
*/
|
||||
public static final String KEY_MAX_ACTIVE_SESSIONS = "max_active_sessions";
|
||||
public static int DEFAULT_MAX_ACTIVE_SESSIONS = 250;
|
||||
public static int MAX_ACTIVE_SESSIONS = DEFAULT_MAX_ACTIVE_SESSIONS;
|
||||
|
||||
/**
|
||||
* Denotes the maximum number of committed blobs per app at any time.
|
||||
*/
|
||||
public static final String KEY_MAX_COMMITTED_BLOBS = "max_committed_blobs";
|
||||
public static int DEFAULT_MAX_COMMITTED_BLOBS = 1000;
|
||||
public static int MAX_COMMITTED_BLOBS = DEFAULT_MAX_COMMITTED_BLOBS;
|
||||
|
||||
/**
|
||||
* Denotes the maximum number of leased blobs per app at any time.
|
||||
*/
|
||||
public static final String KEY_MAX_LEASED_BLOBS = "max_leased_blobs";
|
||||
public static int DEFAULT_MAX_LEASED_BLOBS = 500;
|
||||
public static int MAX_LEASED_BLOBS = DEFAULT_MAX_LEASED_BLOBS;
|
||||
|
||||
/**
|
||||
* Denotes the maximum number of packages explicitly permitted to access a blob
|
||||
* (permitted as part of creating a {@link BlobAccessMode}).
|
||||
*/
|
||||
public static final String KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = "max_permitted_pks";
|
||||
public static int DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = 300;
|
||||
public static int MAX_BLOB_ACCESS_PERMITTED_PACKAGES =
|
||||
DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
|
||||
|
||||
static void refresh(Properties properties) {
|
||||
if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) {
|
||||
return;
|
||||
@@ -178,6 +208,19 @@ class BlobStoreConfig {
|
||||
DELETE_ON_LAST_LEASE_DELAY_MS = properties.getLong(key,
|
||||
DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS);
|
||||
break;
|
||||
case KEY_MAX_ACTIVE_SESSIONS:
|
||||
MAX_ACTIVE_SESSIONS = properties.getInt(key, DEFAULT_MAX_ACTIVE_SESSIONS);
|
||||
break;
|
||||
case KEY_MAX_COMMITTED_BLOBS:
|
||||
MAX_COMMITTED_BLOBS = properties.getInt(key, DEFAULT_MAX_COMMITTED_BLOBS);
|
||||
break;
|
||||
case KEY_MAX_LEASED_BLOBS:
|
||||
MAX_LEASED_BLOBS = properties.getInt(key, DEFAULT_MAX_LEASED_BLOBS);
|
||||
break;
|
||||
case KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES:
|
||||
MAX_BLOB_ACCESS_PERMITTED_PACKAGES = properties.getInt(key,
|
||||
DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES);
|
||||
break;
|
||||
default:
|
||||
Slog.wtf(TAG, "Unknown key in device config properties: " + key);
|
||||
}
|
||||
@@ -210,6 +253,15 @@ class BlobStoreConfig {
|
||||
fout.println(String.format(dumpFormat, KEY_DELETE_ON_LAST_LEASE_DELAY_MS,
|
||||
TimeUtils.formatDuration(DELETE_ON_LAST_LEASE_DELAY_MS),
|
||||
TimeUtils.formatDuration(DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS)));
|
||||
fout.println(String.format(dumpFormat, KEY_MAX_ACTIVE_SESSIONS,
|
||||
MAX_ACTIVE_SESSIONS, DEFAULT_MAX_ACTIVE_SESSIONS));
|
||||
fout.println(String.format(dumpFormat, KEY_MAX_COMMITTED_BLOBS,
|
||||
MAX_COMMITTED_BLOBS, DEFAULT_MAX_COMMITTED_BLOBS));
|
||||
fout.println(String.format(dumpFormat, KEY_MAX_LEASED_BLOBS,
|
||||
MAX_LEASED_BLOBS, DEFAULT_MAX_LEASED_BLOBS));
|
||||
fout.println(String.format(dumpFormat, KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
|
||||
MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
|
||||
DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +340,34 @@ class BlobStoreConfig {
|
||||
return DeviceConfigProperties.DELETE_ON_LAST_LEASE_DELAY_MS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of active sessions per app.
|
||||
*/
|
||||
public static int getMaxActiveSessions() {
|
||||
return DeviceConfigProperties.MAX_ACTIVE_SESSIONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of committed blobs per app.
|
||||
*/
|
||||
public static int getMaxCommittedBlobs() {
|
||||
return DeviceConfigProperties.MAX_COMMITTED_BLOBS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of leased blobs per app.
|
||||
*/
|
||||
public static int getMaxLeasedBlobs() {
|
||||
return DeviceConfigProperties.MAX_LEASED_BLOBS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of packages explicitly permitted to access a blob.
|
||||
*/
|
||||
public static int getMaxPermittedPackages() {
|
||||
return DeviceConfigProperties.MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File prepareBlobFile(long sessionId) {
|
||||
final File blobsDir = prepareBlobsDir();
|
||||
|
||||
@@ -35,6 +35,9 @@ import static com.android.server.blob.BlobStoreConfig.TAG;
|
||||
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
|
||||
import static com.android.server.blob.BlobStoreConfig.getAdjustedCommitTimeMs;
|
||||
import static com.android.server.blob.BlobStoreConfig.getDeletionOnLastLeaseDelayMs;
|
||||
import static com.android.server.blob.BlobStoreConfig.getMaxActiveSessions;
|
||||
import static com.android.server.blob.BlobStoreConfig.getMaxCommittedBlobs;
|
||||
import static com.android.server.blob.BlobStoreConfig.getMaxLeasedBlobs;
|
||||
import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED;
|
||||
import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
|
||||
import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
|
||||
@@ -124,6 +127,7 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@@ -332,9 +336,26 @@ public class BlobStoreManagerService extends SystemService {
|
||||
mKnownBlobIds.add(id);
|
||||
}
|
||||
|
||||
@GuardedBy("mBlobsLock")
|
||||
private int getSessionsCountLocked(int uid, String packageName) {
|
||||
// TODO: Maintain a counter instead of traversing all the sessions
|
||||
final AtomicInteger sessionsCount = new AtomicInteger(0);
|
||||
forEachSessionInUser(session -> {
|
||||
if (session.getOwnerUid() == uid && session.getOwnerPackageName().equals(packageName)) {
|
||||
sessionsCount.getAndIncrement();
|
||||
}
|
||||
}, UserHandle.getUserId(uid));
|
||||
return sessionsCount.get();
|
||||
}
|
||||
|
||||
private long createSessionInternal(BlobHandle blobHandle,
|
||||
int callingUid, String callingPackage) {
|
||||
synchronized (mBlobsLock) {
|
||||
final int sessionsCount = getSessionsCountLocked(callingUid, callingPackage);
|
||||
if (sessionsCount >= getMaxActiveSessions()) {
|
||||
throw new LimitExceededException("Too many active sessions for the caller: "
|
||||
+ sessionsCount);
|
||||
}
|
||||
// TODO: throw if there is already an active session associated with blobHandle.
|
||||
final long sessionId = generateNextSessionIdLocked();
|
||||
final BlobStoreSession session = new BlobStoreSession(mContext,
|
||||
@@ -408,10 +429,39 @@ public class BlobStoreManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mBlobsLock")
|
||||
private int getCommittedBlobsCountLocked(int uid, String packageName) {
|
||||
// TODO: Maintain a counter instead of traversing all the blobs
|
||||
final AtomicInteger blobsCount = new AtomicInteger(0);
|
||||
forEachBlobInUser((blobMetadata) -> {
|
||||
if (blobMetadata.isACommitter(packageName, uid)) {
|
||||
blobsCount.getAndIncrement();
|
||||
}
|
||||
}, UserHandle.getUserId(uid));
|
||||
return blobsCount.get();
|
||||
}
|
||||
|
||||
@GuardedBy("mBlobsLock")
|
||||
private int getLeasedBlobsCountLocked(int uid, String packageName) {
|
||||
// TODO: Maintain a counter instead of traversing all the blobs
|
||||
final AtomicInteger blobsCount = new AtomicInteger(0);
|
||||
forEachBlobInUser((blobMetadata) -> {
|
||||
if (blobMetadata.isALeasee(packageName, uid)) {
|
||||
blobsCount.getAndIncrement();
|
||||
}
|
||||
}, UserHandle.getUserId(uid));
|
||||
return blobsCount.get();
|
||||
}
|
||||
|
||||
private void acquireLeaseInternal(BlobHandle blobHandle, int descriptionResId,
|
||||
CharSequence description, long leaseExpiryTimeMillis,
|
||||
int callingUid, String callingPackage) {
|
||||
synchronized (mBlobsLock) {
|
||||
final int leasesCount = getLeasedBlobsCountLocked(callingUid, callingPackage);
|
||||
if (leasesCount >= getMaxLeasedBlobs()) {
|
||||
throw new LimitExceededException("Too many leased blobs for the caller: "
|
||||
+ leasesCount);
|
||||
}
|
||||
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
|
||||
.get(blobHandle);
|
||||
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
|
||||
@@ -626,6 +676,18 @@ public class BlobStoreManagerService extends SystemService {
|
||||
break;
|
||||
case STATE_VERIFIED_VALID:
|
||||
synchronized (mBlobsLock) {
|
||||
final int committedBlobsCount = getCommittedBlobsCountLocked(
|
||||
session.getOwnerUid(), session.getOwnerPackageName());
|
||||
if (committedBlobsCount >= getMaxCommittedBlobs()) {
|
||||
Slog.d(TAG, "Failed to commit: too many committed blobs. count: "
|
||||
+ committedBlobsCount + "; blob: " + session);
|
||||
session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
|
||||
session.getSessionFile().delete();
|
||||
mActiveBlobIds.remove(session.getSessionId());
|
||||
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
|
||||
.remove(session.getSessionId());
|
||||
break;
|
||||
}
|
||||
final int userId = UserHandle.getUserId(session.getOwnerUid());
|
||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
|
||||
userId);
|
||||
@@ -656,7 +718,7 @@ public class BlobStoreManagerService extends SystemService {
|
||||
} else {
|
||||
blob.addOrReplaceCommitter(existingCommitter);
|
||||
}
|
||||
Slog.d(TAG, "Error committing the blob", e);
|
||||
Slog.d(TAG, "Error committing the blob: " + session, e);
|
||||
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
|
||||
session.getOwnerUid(), blob.getBlobId(), blob.getSize(),
|
||||
FrameworkStatsLog.BLOB_COMMITTED__RESULT__ERROR_DURING_COMMIT);
|
||||
@@ -1096,6 +1158,16 @@ public class BlobStoreManagerService extends SystemService {
|
||||
|
||||
void runClearAllSessions(@UserIdInt int userId) {
|
||||
synchronized (mBlobsLock) {
|
||||
for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
|
||||
final int sessionUserId = mSessions.keyAt(i);
|
||||
if (userId != UserHandle.USER_ALL && userId != sessionUserId) {
|
||||
continue;
|
||||
}
|
||||
final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
|
||||
for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
|
||||
mActiveBlobIds.remove(userSessions.valueAt(j).getSessionId());
|
||||
}
|
||||
}
|
||||
if (userId == UserHandle.USER_ALL) {
|
||||
mSessions.clear();
|
||||
} else {
|
||||
@@ -1107,6 +1179,16 @@ public class BlobStoreManagerService extends SystemService {
|
||||
|
||||
void runClearAllBlobs(@UserIdInt int userId) {
|
||||
synchronized (mBlobsLock) {
|
||||
for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
|
||||
final int blobUserId = mBlobsMap.keyAt(i);
|
||||
if (userId != UserHandle.USER_ALL && userId != blobUserId) {
|
||||
continue;
|
||||
}
|
||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
|
||||
for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
|
||||
mActiveBlobIds.remove(userBlobs.valueAt(j).getBlobId());
|
||||
}
|
||||
}
|
||||
if (userId == UserHandle.USER_ALL) {
|
||||
mBlobsMap.clear();
|
||||
} else {
|
||||
@@ -1331,8 +1413,11 @@ public class BlobStoreManagerService extends SystemService {
|
||||
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
|
||||
}
|
||||
|
||||
// TODO: Verify caller request is within limits (no. of calls/blob sessions/blobs)
|
||||
return createSessionInternal(blobHandle, callingUid, packageName);
|
||||
try {
|
||||
return createSessionInternal(blobHandle, callingUid, packageName);
|
||||
} catch (LimitExceededException e) {
|
||||
throw new ParcelableException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,6 +32,7 @@ import static android.text.format.Formatter.formatFileSize;
|
||||
|
||||
import static com.android.server.blob.BlobStoreConfig.TAG;
|
||||
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_SESSION_CREATION_TIME;
|
||||
import static com.android.server.blob.BlobStoreConfig.getMaxPermittedPackages;
|
||||
import static com.android.server.blob.BlobStoreConfig.hasSessionExpired;
|
||||
|
||||
import android.annotation.BytesLong;
|
||||
@@ -43,7 +44,9 @@ import android.app.blob.IBlobStoreSession;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
import android.os.FileUtils;
|
||||
import android.os.LimitExceededException;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ParcelableException;
|
||||
import android.os.RemoteException;
|
||||
import android.os.RevocableFileDescriptor;
|
||||
import android.os.Trace;
|
||||
@@ -76,7 +79,10 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/** TODO: add doc */
|
||||
/**
|
||||
* Class to represent the state corresponding to an ongoing
|
||||
* {@link android.app.blob.BlobStoreManager.Session}
|
||||
*/
|
||||
@VisibleForTesting
|
||||
class BlobStoreSession extends IBlobStoreSession.Stub {
|
||||
|
||||
@@ -326,6 +332,11 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
|
||||
throw new IllegalStateException("Not allowed to change access type in state: "
|
||||
+ stateToString(mState));
|
||||
}
|
||||
if (mBlobAccessMode.getNumWhitelistedPackages() >= getMaxPermittedPackages()) {
|
||||
throw new ParcelableException(new LimitExceededException(
|
||||
"Too many packages permitted to access the blob: "
|
||||
+ mBlobAccessMode.getNumWhitelistedPackages()));
|
||||
}
|
||||
mBlobAccessMode.allowPackageAccess(packageName, certificate);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user