Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e9232d6d authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Update BlobStoreMS to augment storage stats with blobs data.

- Any pending sessions data is attributed to the apps
  which contributed them.
- Any commited blobs data is attributed to the app which
  has a lease on it. If multiple apps have lease on a blob, don't
  attribute the blob to those apps for now.
- Remove StorageStatsAugmenter.augmentStatsForUser as it
  is not used for anything currently.
- Fix an issue in how we override existing committers and leasees.

Bug: 148694869
Test: atest cts/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
Test: atest tests/tests/os/src/android/os/storage/cts/StorageStatsManagerTest.java
Test: atest hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
Test: manual
Change-Id: Ia4af0a2549c75db66741f2d1979de95d2d150bc8
parent f462373a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -23,5 +23,6 @@ java_library {
    libs: [
        "framework",
        "services.core",
        "services.usage",
    ],
}
+73 −4
Original line number Diff line number Diff line
@@ -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;
@@ -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) {
@@ -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);
        }
    }
@@ -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);
        }
    }
@@ -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.
@@ -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);
+72 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -101,6 +104,8 @@ import java.util.Arrays;
import java.util.List;
import java.util.Objects;
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.
@@ -164,6 +169,8 @@ public class BlobStoreManagerService extends SystemService {

        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
        registerReceivers();
        LocalServices.getService(StorageStatsManagerInternal.class)
                .registerStorageStatsAugmenter(new BlobStorageStatsAugmenter(), TAG);
    }

    @Override
@@ -946,6 +953,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) {
+2 −0
Original line number Diff line number Diff line
@@ -2224,6 +2224,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 {
+4 −0
Original line number Diff line number Diff line
@@ -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)) {
@@ -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