Loading apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java +18 −11 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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(); Loading Loading @@ -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. * Loading Loading @@ -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. * Loading Loading @@ -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. * Loading Loading @@ -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. * Loading Loading @@ -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 { Loading @@ -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(); Loading apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java +80 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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)); } } Loading Loading @@ -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(); Loading apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +88 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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( Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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 { Loading @@ -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 { Loading Loading @@ -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) try { return createSessionInternal(blobHandle, callingUid, packageName); } catch (LimitExceededException e) { throw new ParcelableException(e); } } @Override Loading apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java +12 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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); } } Loading Loading
apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java +18 −11 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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(); Loading Loading @@ -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. * Loading Loading @@ -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. * Loading Loading @@ -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. * Loading Loading @@ -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. * Loading Loading @@ -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 { Loading @@ -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(); Loading
apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java +80 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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)); } } Loading Loading @@ -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(); Loading
apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +88 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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( Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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 { Loading @@ -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 { Loading Loading @@ -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) try { return createSessionInternal(blobHandle, callingUid, packageName); } catch (LimitExceededException e) { throw new ParcelableException(e); } } @Override Loading
apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java +12 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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); } } Loading