Loading apex/blobstore/service/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -23,5 +23,6 @@ java_library { libs: [ "framework", "services.core", "services.usage", ], } apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +73 −4 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static android.app.blob.XmlTags.TAG_ACCESS_MODE; import static android.app.blob.XmlTags.TAG_BLOB_HANDLE; import static android.app.blob.XmlTags.TAG_COMMITTER; import static android.app.blob.XmlTags.TAG_LEASEE; import static android.os.Process.INVALID_UID; import static android.system.OsConstants.O_RDONLY; import static com.android.server.blob.BlobStoreConfig.TAG; Loading Loading @@ -88,7 +89,7 @@ class BlobMetadata { private final ArrayMap<String, ArraySet<RevocableFileDescriptor>> mRevocableFds = new ArrayMap<>(); // Do not access this directly, instead use getSessionFile(). // Do not access this directly, instead use #getBlobFile(). private File mBlobFile; BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) { Loading @@ -112,12 +113,16 @@ class BlobMetadata { void addCommitter(@NonNull Committer committer) { synchronized (mMetadataLock) { // We need to override the committer data, so first remove any existing // committer before adding the new one. mCommitters.remove(committer); mCommitters.add(committer); } } void addCommitters(ArraySet<Committer> committers) { synchronized (mMetadataLock) { mCommitters.clear(); mCommitters.addAll(committers); } } Loading Loading @@ -147,13 +152,18 @@ class BlobMetadata { void addLeasee(String callingPackage, int callingUid, int descriptionResId, CharSequence description, long leaseExpiryTimeMillis) { synchronized (mMetadataLock) { mLeasees.add(new Leasee(callingPackage, callingUid, descriptionResId, description, leaseExpiryTimeMillis)); // We need to override the leasee data, so first remove any existing // leasee before adding the new one. final Leasee leasee = new Leasee(callingPackage, callingUid, descriptionResId, description, leaseExpiryTimeMillis); mLeasees.remove(leasee); mLeasees.add(leasee); } } void addLeasees(ArraySet<Leasee> leasees) { synchronized (mMetadataLock) { mLeasees.clear(); mLeasees.addAll(leasees); } } Loading @@ -178,7 +188,11 @@ class BlobMetadata { } } boolean isAccessAllowedForCaller(String callingPackage, int callingUid) { long getSize() { return getBlobFile().length(); } boolean isAccessAllowedForCaller(@NonNull String callingPackage, int callingUid) { // TODO: verify blob is still valid (expiryTime is not elapsed) synchronized (mMetadataLock) { // Check if packageName already holds a lease on the blob. Loading Loading @@ -209,6 +223,61 @@ class BlobMetadata { return false; } boolean isALeasee(@NonNull String packageName) { return isALeasee(packageName, INVALID_UID); } boolean isALeasee(int uid) { return isALeasee(null, uid); } boolean hasOtherLeasees(@NonNull String packageName) { return hasOtherLeasees(packageName, INVALID_UID); } boolean hasOtherLeasees(int uid) { return hasOtherLeasees(null, uid); } private 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) { final Leasee leasee = mLeasees.valueAt(i); if (packageName != null && uid != INVALID_UID && leasee.equals(packageName, uid)) { return true; } else if (packageName != null && leasee.packageName.equals(packageName)) { return true; } else if (uid != INVALID_UID && leasee.uid == uid) { return true; } } } return false; } private boolean hasOtherLeasees(@Nullable String packageName, int uid) { synchronized (mMetadataLock) { if (mCommitters.size() > 1 || mLeasees.size() > 1) { return true; } for (int i = 0, size = mLeasees.size(); i < size; ++i) { final Leasee leasee = mLeasees.valueAt(i); // TODO: Also exclude packages which are signed with same cert? if (packageName != null && uid != INVALID_UID && !leasee.equals(packageName, uid)) { return true; } else if (packageName != null && !leasee.packageName.equals(packageName)) { return true; } else if (uid != INVALID_UID && leasee.uid != uid) { return true; } } } return false; } File getBlobFile() { if (mBlobFile == null) { mBlobFile = BlobStoreConfig.getBlobFile(mBlobId); Loading apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +72 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageStats; import android.content.res.ResourceId; import android.os.Binder; import android.os.Handler; Loading Loading @@ -85,6 +86,8 @@ import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.Watchdog; import com.android.server.blob.BlobMetadata.Committer; import com.android.server.usage.StorageStatsManagerInternal; import com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; Loading @@ -103,6 +106,8 @@ import java.util.List; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; /** * Service responsible for maintaining and facilitating access to data blobs published by apps. Loading Loading @@ -173,6 +178,8 @@ public class BlobStoreManagerService extends SystemService { mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); registerReceivers(); LocalServices.getService(StorageStatsManagerInternal.class) .registerStorageStatsAugmenter(new BlobStorageStatsAugmenter(), TAG); } @Override Loading Loading @@ -977,6 +984,71 @@ public class BlobStoreManagerService extends SystemService { } } private class BlobStorageStatsAugmenter implements StorageStatsAugmenter { @Override public void augmentStatsForPackage(@NonNull PackageStats stats, @NonNull String packageName, @UserIdInt int userId, boolean callerHasStatsPermission) { final AtomicLong blobsDataSize = new AtomicLong(0); forEachSessionInUser(session -> { if (session.getOwnerPackageName().equals(packageName)) { blobsDataSize.getAndAdd(session.getSize()); } }, userId); forEachBlobInUser(blobMetadata -> { if (blobMetadata.isALeasee(packageName)) { if (!blobMetadata.hasOtherLeasees(packageName) || !callerHasStatsPermission) { blobsDataSize.getAndAdd(blobMetadata.getSize()); } } }, userId); stats.dataSize += blobsDataSize.get(); } @Override public void augmentStatsForUid(@NonNull PackageStats stats, int uid, boolean callerHasStatsPermission) { final int userId = UserHandle.getUserId(uid); final AtomicLong blobsDataSize = new AtomicLong(0); forEachSessionInUser(session -> { if (session.getOwnerUid() == uid) { blobsDataSize.getAndAdd(session.getSize()); } }, userId); forEachBlobInUser(blobMetadata -> { if (blobMetadata.isALeasee(uid)) { if (!blobMetadata.hasOtherLeasees(uid) || !callerHasStatsPermission) { blobsDataSize.getAndAdd(blobMetadata.getSize()); } } }, userId); stats.dataSize += blobsDataSize.get(); } } private void forEachSessionInUser(Consumer<BlobStoreSession> consumer, int userId) { synchronized (mBlobsLock) { final LongSparseArray<BlobStoreSession> userSessions = getUserSessionsLocked(userId); for (int i = 0, count = userSessions.size(); i < count; ++i) { final BlobStoreSession session = userSessions.valueAt(i); consumer.accept(session); } } } private void forEachBlobInUser(Consumer<BlobMetadata> consumer, int userId) { synchronized (mBlobsLock) { final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId); for (int i = 0, count = userBlobs.size(); i < count; ++i) { final BlobMetadata blobMetadata = userBlobs.valueAt(i); consumer.accept(blobMetadata); } } } private class PackageChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Loading api/test-current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -2233,6 +2233,8 @@ package android.os { public final class FileUtils { method public static boolean contains(java.io.File, java.io.File); method @NonNull public static byte[] digest(@NonNull java.io.File, @NonNull String) throws java.io.IOException, java.security.NoSuchAlgorithmException; method @NonNull public static byte[] digest(@NonNull java.io.InputStream, @NonNull String) throws java.io.IOException, java.security.NoSuchAlgorithmException; } public class HidlMemory implements java.io.Closeable { Loading core/java/android/os/FileUtils.java +4 −0 Original line number Diff line number Diff line Loading @@ -743,6 +743,8 @@ public final class FileUtils { * {@link MessageDigest#getInstance(String)}. * @hide */ @TestApi @NonNull public static byte[] digest(@NonNull File file, @NonNull String algorithm) throws IOException, NoSuchAlgorithmException { try (FileInputStream in = new FileInputStream(file)) { Loading @@ -757,6 +759,8 @@ public final class FileUtils { * {@link MessageDigest#getInstance(String)}. * @hide */ @TestApi @NonNull public static byte[] digest(@NonNull InputStream in, @NonNull String algorithm) throws IOException, NoSuchAlgorithmException { // TODO: implement kernel optimizations Loading Loading
apex/blobstore/service/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -23,5 +23,6 @@ java_library { libs: [ "framework", "services.core", "services.usage", ], }
apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +73 −4 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static android.app.blob.XmlTags.TAG_ACCESS_MODE; import static android.app.blob.XmlTags.TAG_BLOB_HANDLE; import static android.app.blob.XmlTags.TAG_COMMITTER; import static android.app.blob.XmlTags.TAG_LEASEE; import static android.os.Process.INVALID_UID; import static android.system.OsConstants.O_RDONLY; import static com.android.server.blob.BlobStoreConfig.TAG; Loading Loading @@ -88,7 +89,7 @@ class BlobMetadata { private final ArrayMap<String, ArraySet<RevocableFileDescriptor>> mRevocableFds = new ArrayMap<>(); // Do not access this directly, instead use getSessionFile(). // Do not access this directly, instead use #getBlobFile(). private File mBlobFile; BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) { Loading @@ -112,12 +113,16 @@ class BlobMetadata { void addCommitter(@NonNull Committer committer) { synchronized (mMetadataLock) { // We need to override the committer data, so first remove any existing // committer before adding the new one. mCommitters.remove(committer); mCommitters.add(committer); } } void addCommitters(ArraySet<Committer> committers) { synchronized (mMetadataLock) { mCommitters.clear(); mCommitters.addAll(committers); } } Loading Loading @@ -147,13 +152,18 @@ class BlobMetadata { void addLeasee(String callingPackage, int callingUid, int descriptionResId, CharSequence description, long leaseExpiryTimeMillis) { synchronized (mMetadataLock) { mLeasees.add(new Leasee(callingPackage, callingUid, descriptionResId, description, leaseExpiryTimeMillis)); // We need to override the leasee data, so first remove any existing // leasee before adding the new one. final Leasee leasee = new Leasee(callingPackage, callingUid, descriptionResId, description, leaseExpiryTimeMillis); mLeasees.remove(leasee); mLeasees.add(leasee); } } void addLeasees(ArraySet<Leasee> leasees) { synchronized (mMetadataLock) { mLeasees.clear(); mLeasees.addAll(leasees); } } Loading @@ -178,7 +188,11 @@ class BlobMetadata { } } boolean isAccessAllowedForCaller(String callingPackage, int callingUid) { long getSize() { return getBlobFile().length(); } boolean isAccessAllowedForCaller(@NonNull String callingPackage, int callingUid) { // TODO: verify blob is still valid (expiryTime is not elapsed) synchronized (mMetadataLock) { // Check if packageName already holds a lease on the blob. Loading Loading @@ -209,6 +223,61 @@ class BlobMetadata { return false; } boolean isALeasee(@NonNull String packageName) { return isALeasee(packageName, INVALID_UID); } boolean isALeasee(int uid) { return isALeasee(null, uid); } boolean hasOtherLeasees(@NonNull String packageName) { return hasOtherLeasees(packageName, INVALID_UID); } boolean hasOtherLeasees(int uid) { return hasOtherLeasees(null, uid); } private 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) { final Leasee leasee = mLeasees.valueAt(i); if (packageName != null && uid != INVALID_UID && leasee.equals(packageName, uid)) { return true; } else if (packageName != null && leasee.packageName.equals(packageName)) { return true; } else if (uid != INVALID_UID && leasee.uid == uid) { return true; } } } return false; } private boolean hasOtherLeasees(@Nullable String packageName, int uid) { synchronized (mMetadataLock) { if (mCommitters.size() > 1 || mLeasees.size() > 1) { return true; } for (int i = 0, size = mLeasees.size(); i < size; ++i) { final Leasee leasee = mLeasees.valueAt(i); // TODO: Also exclude packages which are signed with same cert? if (packageName != null && uid != INVALID_UID && !leasee.equals(packageName, uid)) { return true; } else if (packageName != null && !leasee.packageName.equals(packageName)) { return true; } else if (uid != INVALID_UID && leasee.uid != uid) { return true; } } } return false; } File getBlobFile() { if (mBlobFile == null) { mBlobFile = BlobStoreConfig.getBlobFile(mBlobId); Loading
apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +72 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageStats; import android.content.res.ResourceId; import android.os.Binder; import android.os.Handler; Loading Loading @@ -85,6 +86,8 @@ import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.Watchdog; import com.android.server.blob.BlobMetadata.Committer; import com.android.server.usage.StorageStatsManagerInternal; import com.android.server.usage.StorageStatsManagerInternal.StorageStatsAugmenter; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; Loading @@ -103,6 +106,8 @@ import java.util.List; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; /** * Service responsible for maintaining and facilitating access to data blobs published by apps. Loading Loading @@ -173,6 +178,8 @@ public class BlobStoreManagerService extends SystemService { mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); registerReceivers(); LocalServices.getService(StorageStatsManagerInternal.class) .registerStorageStatsAugmenter(new BlobStorageStatsAugmenter(), TAG); } @Override Loading Loading @@ -977,6 +984,71 @@ public class BlobStoreManagerService extends SystemService { } } private class BlobStorageStatsAugmenter implements StorageStatsAugmenter { @Override public void augmentStatsForPackage(@NonNull PackageStats stats, @NonNull String packageName, @UserIdInt int userId, boolean callerHasStatsPermission) { final AtomicLong blobsDataSize = new AtomicLong(0); forEachSessionInUser(session -> { if (session.getOwnerPackageName().equals(packageName)) { blobsDataSize.getAndAdd(session.getSize()); } }, userId); forEachBlobInUser(blobMetadata -> { if (blobMetadata.isALeasee(packageName)) { if (!blobMetadata.hasOtherLeasees(packageName) || !callerHasStatsPermission) { blobsDataSize.getAndAdd(blobMetadata.getSize()); } } }, userId); stats.dataSize += blobsDataSize.get(); } @Override public void augmentStatsForUid(@NonNull PackageStats stats, int uid, boolean callerHasStatsPermission) { final int userId = UserHandle.getUserId(uid); final AtomicLong blobsDataSize = new AtomicLong(0); forEachSessionInUser(session -> { if (session.getOwnerUid() == uid) { blobsDataSize.getAndAdd(session.getSize()); } }, userId); forEachBlobInUser(blobMetadata -> { if (blobMetadata.isALeasee(uid)) { if (!blobMetadata.hasOtherLeasees(uid) || !callerHasStatsPermission) { blobsDataSize.getAndAdd(blobMetadata.getSize()); } } }, userId); stats.dataSize += blobsDataSize.get(); } } private void forEachSessionInUser(Consumer<BlobStoreSession> consumer, int userId) { synchronized (mBlobsLock) { final LongSparseArray<BlobStoreSession> userSessions = getUserSessionsLocked(userId); for (int i = 0, count = userSessions.size(); i < count; ++i) { final BlobStoreSession session = userSessions.valueAt(i); consumer.accept(session); } } } private void forEachBlobInUser(Consumer<BlobMetadata> consumer, int userId) { synchronized (mBlobsLock) { final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId); for (int i = 0, count = userBlobs.size(); i < count; ++i) { final BlobMetadata blobMetadata = userBlobs.valueAt(i); consumer.accept(blobMetadata); } } } private class PackageChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Loading
api/test-current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -2233,6 +2233,8 @@ package android.os { public final class FileUtils { method public static boolean contains(java.io.File, java.io.File); method @NonNull public static byte[] digest(@NonNull java.io.File, @NonNull String) throws java.io.IOException, java.security.NoSuchAlgorithmException; method @NonNull public static byte[] digest(@NonNull java.io.InputStream, @NonNull String) throws java.io.IOException, java.security.NoSuchAlgorithmException; } public class HidlMemory implements java.io.Closeable { Loading
core/java/android/os/FileUtils.java +4 −0 Original line number Diff line number Diff line Loading @@ -743,6 +743,8 @@ public final class FileUtils { * {@link MessageDigest#getInstance(String)}. * @hide */ @TestApi @NonNull public static byte[] digest(@NonNull File file, @NonNull String algorithm) throws IOException, NoSuchAlgorithmException { try (FileInputStream in = new FileInputStream(file)) { Loading @@ -757,6 +759,8 @@ public final class FileUtils { * {@link MessageDigest#getInstance(String)}. * @hide */ @TestApi @NonNull public static byte[] digest(@NonNull InputStream in, @NonNull String algorithm) throws IOException, NoSuchAlgorithmException { // TODO: implement kernel optimizations Loading