Loading apex/blobstore/framework/java/android/app/blob/BlobHandle.java +1 −1 Original line number Diff line number Diff line Loading @@ -26,8 +26,8 @@ import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.util.Base64; import android.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; Loading apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java +1 −1 Original line number Diff line number Diff line Loading @@ -37,9 +37,9 @@ import android.permission.PermissionManager; import android.util.ArraySet; import android.util.Base64; import android.util.DebugUtils; import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; Loading apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +180 −64 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.server.blob; import static android.Manifest.permission.ACCESS_BLOBS_ACROSS_USERS; import static android.app.blob.XmlTags.ATTR_COMMIT_TIME_MS; import static android.app.blob.XmlTags.ATTR_DESCRIPTION; import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME; Loading @@ -36,6 +37,7 @@ import static com.android.server.blob.BlobStoreConfig.TAG; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_COMMIT_TIME; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_DESC_RES_NAME; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ALLOW_ACCESS_ACROSS_USERS; import static com.android.server.blob.BlobStoreConfig.hasLeaseWaitTimeElapsed; import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId; import static com.android.server.blob.BlobStoreUtils.getPackageResources; Loading @@ -45,15 +47,18 @@ import android.annotation.Nullable; import android.app.blob.BlobHandle; import android.app.blob.LeaseInfo; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.ResourceId; import android.content.res.Resources; import android.os.ParcelFileDescriptor; import android.os.RevocableFileDescriptor; import android.os.UserHandle; import android.permission.PermissionManager; import android.system.ErrnoException; import android.system.Os; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; import android.util.StatsEvent; Loading @@ -62,7 +67,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import com.android.server.blob.BlobStoreManagerService.DumpArgs; Loading @@ -85,7 +89,6 @@ class BlobMetadata { private final long mBlobId; private final BlobHandle mBlobHandle; private final int mUserId; @GuardedBy("mMetadataLock") private final ArraySet<Committer> mCommitters = new ArraySet<>(); Loading @@ -94,24 +97,23 @@ class BlobMetadata { private final ArraySet<Leasee> mLeasees = new ArraySet<>(); /** * Contains packageName -> {RevocableFileDescriptors}. * Contains Accessor -> {RevocableFileDescriptors}. * * Keep track of RevocableFileDescriptors given to clients which are not yet revoked/closed so * that when clients access is revoked or the blob gets deleted, we can be sure that clients * do not have any reference to the blob and the space occupied by the blob can be freed. */ @GuardedBy("mRevocableFds") private final ArrayMap<String, ArraySet<RevocableFileDescriptor>> mRevocableFds = private final ArrayMap<Accessor, ArraySet<RevocableFileDescriptor>> mRevocableFds = new ArrayMap<>(); // Do not access this directly, instead use #getBlobFile(). private File mBlobFile; BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) { BlobMetadata(Context context, long blobId, BlobHandle blobHandle) { mContext = context; this.mBlobId = blobId; this.mBlobHandle = blobHandle; this.mUserId = userId; } long getBlobId() { Loading @@ -122,10 +124,6 @@ class BlobMetadata { return mBlobHandle; } int getUserId() { return mUserId; } void addOrReplaceCommitter(@NonNull Committer committer) { synchronized (mMetadataLock) { // We need to override the committer data, so first remove any existing Loading Loading @@ -155,13 +153,24 @@ class BlobMetadata { } } void removeCommittersFromUnknownPkgs(SparseArray<String> knownPackages) { void removeCommittersFromUnknownPkgs(SparseArray<SparseArray<String>> knownPackages) { synchronized (mMetadataLock) { mCommitters.removeIf(committer -> !committer.packageName.equals(knownPackages.get(committer.uid))); mCommitters.removeIf(committer -> { final int userId = UserHandle.getUserId(committer.uid); final SparseArray<String> userPackages = knownPackages.get(userId); if (userPackages == null) { return true; } return !committer.packageName.equals(userPackages.get(committer.uid)); }); } } void addCommittersAndLeasees(BlobMetadata blobMetadata) { mCommitters.addAll(blobMetadata.mCommitters); mLeasees.addAll(blobMetadata.mLeasees); } @Nullable Committer getExistingCommitter(@NonNull String packageName, int uid) { synchronized (mCommitters) { Loading Loading @@ -201,10 +210,16 @@ class BlobMetadata { } } void removeLeaseesFromUnknownPkgs(SparseArray<String> knownPackages) { void removeLeaseesFromUnknownPkgs(SparseArray<SparseArray<String>> knownPackages) { synchronized (mMetadataLock) { mLeasees.removeIf(leasee -> !leasee.packageName.equals(knownPackages.get(leasee.uid))); mLeasees.removeIf(leasee -> { final int userId = UserHandle.getUserId(leasee.uid); final SparseArray<String> userPackages = knownPackages.get(userId); if (userPackages == null) { return true; } return !leasee.packageName.equals(userPackages.get(leasee.uid)); }); } } Loading @@ -214,6 +229,25 @@ class BlobMetadata { } } void removeDataForUser(int userId) { synchronized (mMetadataLock) { mCommitters.removeIf(committer -> (userId == UserHandle.getUserId(committer.uid))); mLeasees.removeIf(leasee -> (userId == UserHandle.getUserId(leasee.uid))); mRevocableFds.entrySet().removeIf(entry -> { final Accessor accessor = entry.getKey(); final ArraySet<RevocableFileDescriptor> rFds = entry.getValue(); if (userId != UserHandle.getUserId(accessor.uid)) { return false; } for (int i = 0, fdCount = rFds.size(); i < fdCount; ++i) { rFds.valueAt(i).revoke(); } rFds.clear(); return true; }); } } boolean hasValidLeases() { synchronized (mMetadataLock) { for (int i = 0, size = mLeasees.size(); i < size; ++i) { Loading Loading @@ -244,8 +278,12 @@ class BlobMetadata { } } final int callingUserId = UserHandle.getUserId(callingUid); for (int i = 0, size = mCommitters.size(); i < size; ++i) { final Committer committer = mCommitters.valueAt(i); if (callingUserId != UserHandle.getUserId(committer.uid)) { continue; } // Check if the caller is the same package that committed the blob. if (committer.equals(callingPackage, callingUid)) { Loading @@ -259,38 +297,105 @@ class BlobMetadata { return true; } } final boolean canCallerAccessBlobsAcrossUsers = checkCallerCanAccessBlobsAcrossUsers( callingPackage, callingUserId); if (!canCallerAccessBlobsAcrossUsers) { return false; } for (int i = 0, size = mCommitters.size(); i < size; ++i) { final Committer committer = mCommitters.valueAt(i); final int committerUserId = UserHandle.getUserId(committer.uid); if (callingUserId == committerUserId) { continue; } if (!checkCallerCanAccessBlobsAcrossUsers(callingPackage, committerUserId)) { continue; } // Check if the caller is allowed access as per the access mode specified // by the committer. if (committer.blobAccessMode.isAccessAllowedForCaller(mContext, callingPackage, committer.packageName, callingUid, attributionTag)) { return true; } } } return false; } private static boolean checkCallerCanAccessBlobsAcrossUsers( String callingPackage, int callingUserId) { return PermissionManager.checkPackageNamePermission(ACCESS_BLOBS_ACROSS_USERS, callingPackage, callingUserId) == PackageManager.PERMISSION_GRANTED; } boolean hasACommitterOrLeaseeInUser(int userId) { return hasACommitterInUser(userId) || hasALeaseeInUser(userId); } boolean hasACommitterInUser(int userId) { synchronized (mMetadataLock) { for (int i = 0, size = mCommitters.size(); i < size; ++i) { final Committer committer = mCommitters.valueAt(i); if (userId == UserHandle.getUserId(committer.uid)) { return true; } } } return false; } private boolean hasALeaseeInUser(int userId) { synchronized (mMetadataLock) { for (int i = 0, size = mLeasees.size(); i < size; ++i) { final Leasee leasee = mLeasees.valueAt(i); if (userId == UserHandle.getUserId(leasee.uid)) { return true; } } } return false; } boolean isACommitter(@NonNull String packageName, int uid) { synchronized (mMetadataLock) { return isAnAccessor(mCommitters, packageName, uid); return isAnAccessor(mCommitters, packageName, uid, UserHandle.getUserId(uid)); } } boolean isALeasee(@Nullable String packageName, int uid) { synchronized (mMetadataLock) { final Leasee leasee = getAccessor(mLeasees, packageName, uid); final Leasee leasee = getAccessor(mLeasees, packageName, uid, UserHandle.getUserId(uid)); return leasee != null && leasee.isStillValid(); } } private boolean isALeaseeInUser(@Nullable String packageName, int uid, int userId) { synchronized (mMetadataLock) { final Leasee leasee = getAccessor(mLeasees, packageName, uid, userId); return leasee != null && leasee.isStillValid(); } } private static <T extends Accessor> boolean isAnAccessor(@NonNull ArraySet<T> accessors, @Nullable String packageName, int uid) { @Nullable String packageName, int uid, int userId) { // Check if the package is an accessor of the data blob. return getAccessor(accessors, packageName, uid) != null; return getAccessor(accessors, packageName, uid, userId) != null; } private static <T extends Accessor> T getAccessor(@NonNull ArraySet<T> accessors, @Nullable String packageName, int uid) { @Nullable String packageName, int uid, int userId) { // Check if the package is an accessor of the data blob. for (int i = 0, size = accessors.size(); i < size; ++i) { final Accessor accessor = accessors.valueAt(i); if (packageName != null && uid != INVALID_UID && accessor.equals(packageName, uid)) { return (T) accessor; } else if (packageName != null && accessor.packageName.equals(packageName)) { } else if (packageName != null && accessor.packageName.equals(packageName) && userId == UserHandle.getUserId(accessor.uid)) { return (T) accessor; } else if (uid != INVALID_UID && accessor.uid == uid) { return (T) accessor; Loading @@ -299,23 +404,29 @@ class BlobMetadata { return null; } boolean isALeasee(@NonNull String packageName) { return isALeasee(packageName, INVALID_UID); boolean shouldAttributeToLeasee(@NonNull String packageName, int userId, boolean callerHasStatsPermission) { if (!isALeaseeInUser(packageName, INVALID_UID, userId)) { return false; } boolean isALeasee(int uid) { return isALeasee(null, uid); if (!callerHasStatsPermission || !hasOtherLeasees(packageName, INVALID_UID, userId)) { return true; } boolean hasOtherLeasees(@NonNull String packageName) { return hasOtherLeasees(packageName, INVALID_UID); return false; } boolean hasOtherLeasees(int uid) { return hasOtherLeasees(null, uid); boolean shouldAttributeToLeasee(int uid, boolean callerHasStatsPermission) { final int userId = UserHandle.getUserId(uid); if (!isALeaseeInUser(null, uid, userId)) { return false; } if (!callerHasStatsPermission || !hasOtherLeasees(null, uid, userId)) { return true; } return false; } private boolean hasOtherLeasees(@Nullable String packageName, int uid) { private boolean hasOtherLeasees(@Nullable String packageName, int uid, int userId) { synchronized (mMetadataLock) { for (int i = 0, size = mLeasees.size(); i < size; ++i) { final Leasee leasee = mLeasees.valueAt(i); Loading @@ -326,7 +437,8 @@ class BlobMetadata { if (packageName != null && uid != INVALID_UID && !leasee.equals(packageName, uid)) { return true; } else if (packageName != null && !leasee.packageName.equals(packageName)) { } else if (packageName != null && (!leasee.packageName.equals(packageName) || userId != UserHandle.getUserId(leasee.uid))) { return true; } else if (uid != INVALID_UID && leasee.uid != uid) { return true; Loading Loading @@ -371,7 +483,7 @@ class BlobMetadata { return mBlobFile; } ParcelFileDescriptor openForRead(String callingPackage) throws IOException { ParcelFileDescriptor openForRead(String callingPackage, int callingUid) throws IOException { // TODO: Add limit on opened fds FileDescriptor fd; try { Loading @@ -381,7 +493,7 @@ class BlobMetadata { } try { if (BlobStoreConfig.shouldUseRevocableFdForReads()) { return createRevocableFd(fd, callingPackage); return createRevocableFd(fd, callingPackage, callingUid); } else { return new ParcelFileDescriptor(fd); } Loading @@ -393,26 +505,28 @@ class BlobMetadata { @NonNull private ParcelFileDescriptor createRevocableFd(FileDescriptor fd, String callingPackage) throws IOException { String callingPackage, int callingUid) throws IOException { final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd); final Accessor accessor; synchronized (mRevocableFds) { ArraySet<RevocableFileDescriptor> revocableFdsForPkg = mRevocableFds.get(callingPackage); if (revocableFdsForPkg == null) { revocableFdsForPkg = new ArraySet<>(); mRevocableFds.put(callingPackage, revocableFdsForPkg); accessor = new Accessor(callingPackage, callingUid); ArraySet<RevocableFileDescriptor> revocableFdsForAccessor = mRevocableFds.get(accessor); if (revocableFdsForAccessor == null) { revocableFdsForAccessor = new ArraySet<>(); mRevocableFds.put(accessor, revocableFdsForAccessor); } revocableFdsForPkg.add(revocableFd); revocableFdsForAccessor.add(revocableFd); } revocableFd.addOnCloseListener((e) -> { synchronized (mRevocableFds) { final ArraySet<RevocableFileDescriptor> revocableFdsForPkg = mRevocableFds.get(callingPackage); if (revocableFdsForPkg != null) { revocableFdsForPkg.remove(revocableFd); if (revocableFdsForPkg.isEmpty()) { mRevocableFds.remove(callingPackage); final ArraySet<RevocableFileDescriptor> revocableFdsForAccessor = mRevocableFds.get(accessor); if (revocableFdsForAccessor != null) { revocableFdsForAccessor.remove(revocableFd); if (revocableFdsForAccessor.isEmpty()) { mRevocableFds.remove(accessor); } } } Loading @@ -421,22 +535,23 @@ class BlobMetadata { } void destroy() { revokeAllFds(); revokeAndClearAllFds(); getBlobFile().delete(); } private void revokeAllFds() { private void revokeAndClearAllFds() { synchronized (mRevocableFds) { for (int i = 0, pkgCount = mRevocableFds.size(); i < pkgCount; ++i) { final ArraySet<RevocableFileDescriptor> packageFds = for (int i = 0, accessorCount = mRevocableFds.size(); i < accessorCount; ++i) { final ArraySet<RevocableFileDescriptor> rFds = mRevocableFds.valueAt(i); if (packageFds == null) { if (rFds == null) { continue; } for (int j = 0, fdCount = packageFds.size(); j < fdCount; ++j) { packageFds.valueAt(j).revoke(); for (int j = 0, fdCount = rFds.size(); j < fdCount; ++j) { rFds.valueAt(j).revoke(); } } mRevocableFds.clear(); } } Loading Loading @@ -547,10 +662,10 @@ class BlobMetadata { fout.println("<empty>"); } else { for (int i = 0, count = mRevocableFds.size(); i < count; ++i) { final String packageName = mRevocableFds.keyAt(i); final ArraySet<RevocableFileDescriptor> packageFds = final Accessor accessor = mRevocableFds.keyAt(i); final ArraySet<RevocableFileDescriptor> rFds = mRevocableFds.valueAt(i); fout.println(packageName + "#" + packageFds.size()); fout.println(accessor + ": #" + rFds.size()); } } fout.decreaseIndent(); Loading @@ -560,7 +675,6 @@ class BlobMetadata { void writeToXml(XmlSerializer out) throws IOException { synchronized (mMetadataLock) { XmlUtils.writeLongAttribute(out, ATTR_ID, mBlobId); XmlUtils.writeIntAttribute(out, ATTR_USER_ID, mUserId); out.startTag(null, TAG_BLOB_HANDLE); mBlobHandle.writeToXml(out); Loading @@ -584,7 +698,9 @@ class BlobMetadata { static BlobMetadata createFromXml(XmlPullParser in, int version, Context context) throws XmlPullParserException, IOException { final long blobId = XmlUtils.readLongAttribute(in, ATTR_ID); final int userId = XmlUtils.readIntAttribute(in, ATTR_USER_ID); if (version < XML_VERSION_ALLOW_ACCESS_ACROSS_USERS) { XmlUtils.readIntAttribute(in, ATTR_USER_ID); } BlobHandle blobHandle = null; final ArraySet<Committer> committers = new ArraySet<>(); Loading @@ -608,7 +724,7 @@ class BlobMetadata { return null; } final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle, userId); final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle); blobMetadata.setCommitters(committers); blobMetadata.setLeasees(leasees); return blobMetadata; Loading apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java +3 −3 Original line number Diff line number Diff line Loading @@ -27,12 +27,11 @@ import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; import android.text.TextUtils; import android.util.DataUnit; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import android.util.TimeUtils; import com.android.internal.util.IndentingPrintWriter; import java.io.File; import java.util.concurrent.TimeUnit; Loading @@ -47,8 +46,9 @@ class BlobStoreConfig { public static final int XML_VERSION_ADD_DESC_RES_NAME = 3; public static final int XML_VERSION_ADD_COMMIT_TIME = 4; public static final int XML_VERSION_ADD_SESSION_CREATION_TIME = 5; public static final int XML_VERSION_ALLOW_ACCESS_ACROSS_USERS = 6; public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_SESSION_CREATION_TIME; public static final int XML_VERSION_CURRENT = XML_VERSION_ALLOW_ACCESS_ACROSS_USERS; public static final long INVALID_BLOB_ID = 0; public static final long INVALID_BLOB_SIZE = 0; Loading Loading
apex/blobstore/framework/java/android/app/blob/BlobHandle.java +1 −1 Original line number Diff line number Diff line Loading @@ -26,8 +26,8 @@ import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.util.Base64; import android.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; Loading
apex/blobstore/service/java/com/android/server/blob/BlobAccessMode.java +1 −1 Original line number Diff line number Diff line Loading @@ -37,9 +37,9 @@ import android.permission.PermissionManager; import android.util.ArraySet; import android.util.Base64; import android.util.DebugUtils; import android.util.IndentingPrintWriter; import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; Loading
apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +180 −64 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.server.blob; import static android.Manifest.permission.ACCESS_BLOBS_ACROSS_USERS; import static android.app.blob.XmlTags.ATTR_COMMIT_TIME_MS; import static android.app.blob.XmlTags.ATTR_DESCRIPTION; import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME; Loading @@ -36,6 +37,7 @@ import static com.android.server.blob.BlobStoreConfig.TAG; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_COMMIT_TIME; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_DESC_RES_NAME; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC; import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ALLOW_ACCESS_ACROSS_USERS; import static com.android.server.blob.BlobStoreConfig.hasLeaseWaitTimeElapsed; import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId; import static com.android.server.blob.BlobStoreUtils.getPackageResources; Loading @@ -45,15 +47,18 @@ import android.annotation.Nullable; import android.app.blob.BlobHandle; import android.app.blob.LeaseInfo; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.ResourceId; import android.content.res.Resources; import android.os.ParcelFileDescriptor; import android.os.RevocableFileDescriptor; import android.os.UserHandle; import android.permission.PermissionManager; import android.system.ErrnoException; import android.system.Os; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; import android.util.StatsEvent; Loading @@ -62,7 +67,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import com.android.server.blob.BlobStoreManagerService.DumpArgs; Loading @@ -85,7 +89,6 @@ class BlobMetadata { private final long mBlobId; private final BlobHandle mBlobHandle; private final int mUserId; @GuardedBy("mMetadataLock") private final ArraySet<Committer> mCommitters = new ArraySet<>(); Loading @@ -94,24 +97,23 @@ class BlobMetadata { private final ArraySet<Leasee> mLeasees = new ArraySet<>(); /** * Contains packageName -> {RevocableFileDescriptors}. * Contains Accessor -> {RevocableFileDescriptors}. * * Keep track of RevocableFileDescriptors given to clients which are not yet revoked/closed so * that when clients access is revoked or the blob gets deleted, we can be sure that clients * do not have any reference to the blob and the space occupied by the blob can be freed. */ @GuardedBy("mRevocableFds") private final ArrayMap<String, ArraySet<RevocableFileDescriptor>> mRevocableFds = private final ArrayMap<Accessor, ArraySet<RevocableFileDescriptor>> mRevocableFds = new ArrayMap<>(); // Do not access this directly, instead use #getBlobFile(). private File mBlobFile; BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) { BlobMetadata(Context context, long blobId, BlobHandle blobHandle) { mContext = context; this.mBlobId = blobId; this.mBlobHandle = blobHandle; this.mUserId = userId; } long getBlobId() { Loading @@ -122,10 +124,6 @@ class BlobMetadata { return mBlobHandle; } int getUserId() { return mUserId; } void addOrReplaceCommitter(@NonNull Committer committer) { synchronized (mMetadataLock) { // We need to override the committer data, so first remove any existing Loading Loading @@ -155,13 +153,24 @@ class BlobMetadata { } } void removeCommittersFromUnknownPkgs(SparseArray<String> knownPackages) { void removeCommittersFromUnknownPkgs(SparseArray<SparseArray<String>> knownPackages) { synchronized (mMetadataLock) { mCommitters.removeIf(committer -> !committer.packageName.equals(knownPackages.get(committer.uid))); mCommitters.removeIf(committer -> { final int userId = UserHandle.getUserId(committer.uid); final SparseArray<String> userPackages = knownPackages.get(userId); if (userPackages == null) { return true; } return !committer.packageName.equals(userPackages.get(committer.uid)); }); } } void addCommittersAndLeasees(BlobMetadata blobMetadata) { mCommitters.addAll(blobMetadata.mCommitters); mLeasees.addAll(blobMetadata.mLeasees); } @Nullable Committer getExistingCommitter(@NonNull String packageName, int uid) { synchronized (mCommitters) { Loading Loading @@ -201,10 +210,16 @@ class BlobMetadata { } } void removeLeaseesFromUnknownPkgs(SparseArray<String> knownPackages) { void removeLeaseesFromUnknownPkgs(SparseArray<SparseArray<String>> knownPackages) { synchronized (mMetadataLock) { mLeasees.removeIf(leasee -> !leasee.packageName.equals(knownPackages.get(leasee.uid))); mLeasees.removeIf(leasee -> { final int userId = UserHandle.getUserId(leasee.uid); final SparseArray<String> userPackages = knownPackages.get(userId); if (userPackages == null) { return true; } return !leasee.packageName.equals(userPackages.get(leasee.uid)); }); } } Loading @@ -214,6 +229,25 @@ class BlobMetadata { } } void removeDataForUser(int userId) { synchronized (mMetadataLock) { mCommitters.removeIf(committer -> (userId == UserHandle.getUserId(committer.uid))); mLeasees.removeIf(leasee -> (userId == UserHandle.getUserId(leasee.uid))); mRevocableFds.entrySet().removeIf(entry -> { final Accessor accessor = entry.getKey(); final ArraySet<RevocableFileDescriptor> rFds = entry.getValue(); if (userId != UserHandle.getUserId(accessor.uid)) { return false; } for (int i = 0, fdCount = rFds.size(); i < fdCount; ++i) { rFds.valueAt(i).revoke(); } rFds.clear(); return true; }); } } boolean hasValidLeases() { synchronized (mMetadataLock) { for (int i = 0, size = mLeasees.size(); i < size; ++i) { Loading Loading @@ -244,8 +278,12 @@ class BlobMetadata { } } final int callingUserId = UserHandle.getUserId(callingUid); for (int i = 0, size = mCommitters.size(); i < size; ++i) { final Committer committer = mCommitters.valueAt(i); if (callingUserId != UserHandle.getUserId(committer.uid)) { continue; } // Check if the caller is the same package that committed the blob. if (committer.equals(callingPackage, callingUid)) { Loading @@ -259,38 +297,105 @@ class BlobMetadata { return true; } } final boolean canCallerAccessBlobsAcrossUsers = checkCallerCanAccessBlobsAcrossUsers( callingPackage, callingUserId); if (!canCallerAccessBlobsAcrossUsers) { return false; } for (int i = 0, size = mCommitters.size(); i < size; ++i) { final Committer committer = mCommitters.valueAt(i); final int committerUserId = UserHandle.getUserId(committer.uid); if (callingUserId == committerUserId) { continue; } if (!checkCallerCanAccessBlobsAcrossUsers(callingPackage, committerUserId)) { continue; } // Check if the caller is allowed access as per the access mode specified // by the committer. if (committer.blobAccessMode.isAccessAllowedForCaller(mContext, callingPackage, committer.packageName, callingUid, attributionTag)) { return true; } } } return false; } private static boolean checkCallerCanAccessBlobsAcrossUsers( String callingPackage, int callingUserId) { return PermissionManager.checkPackageNamePermission(ACCESS_BLOBS_ACROSS_USERS, callingPackage, callingUserId) == PackageManager.PERMISSION_GRANTED; } boolean hasACommitterOrLeaseeInUser(int userId) { return hasACommitterInUser(userId) || hasALeaseeInUser(userId); } boolean hasACommitterInUser(int userId) { synchronized (mMetadataLock) { for (int i = 0, size = mCommitters.size(); i < size; ++i) { final Committer committer = mCommitters.valueAt(i); if (userId == UserHandle.getUserId(committer.uid)) { return true; } } } return false; } private boolean hasALeaseeInUser(int userId) { synchronized (mMetadataLock) { for (int i = 0, size = mLeasees.size(); i < size; ++i) { final Leasee leasee = mLeasees.valueAt(i); if (userId == UserHandle.getUserId(leasee.uid)) { return true; } } } return false; } boolean isACommitter(@NonNull String packageName, int uid) { synchronized (mMetadataLock) { return isAnAccessor(mCommitters, packageName, uid); return isAnAccessor(mCommitters, packageName, uid, UserHandle.getUserId(uid)); } } boolean isALeasee(@Nullable String packageName, int uid) { synchronized (mMetadataLock) { final Leasee leasee = getAccessor(mLeasees, packageName, uid); final Leasee leasee = getAccessor(mLeasees, packageName, uid, UserHandle.getUserId(uid)); return leasee != null && leasee.isStillValid(); } } private boolean isALeaseeInUser(@Nullable String packageName, int uid, int userId) { synchronized (mMetadataLock) { final Leasee leasee = getAccessor(mLeasees, packageName, uid, userId); return leasee != null && leasee.isStillValid(); } } private static <T extends Accessor> boolean isAnAccessor(@NonNull ArraySet<T> accessors, @Nullable String packageName, int uid) { @Nullable String packageName, int uid, int userId) { // Check if the package is an accessor of the data blob. return getAccessor(accessors, packageName, uid) != null; return getAccessor(accessors, packageName, uid, userId) != null; } private static <T extends Accessor> T getAccessor(@NonNull ArraySet<T> accessors, @Nullable String packageName, int uid) { @Nullable String packageName, int uid, int userId) { // Check if the package is an accessor of the data blob. for (int i = 0, size = accessors.size(); i < size; ++i) { final Accessor accessor = accessors.valueAt(i); if (packageName != null && uid != INVALID_UID && accessor.equals(packageName, uid)) { return (T) accessor; } else if (packageName != null && accessor.packageName.equals(packageName)) { } else if (packageName != null && accessor.packageName.equals(packageName) && userId == UserHandle.getUserId(accessor.uid)) { return (T) accessor; } else if (uid != INVALID_UID && accessor.uid == uid) { return (T) accessor; Loading @@ -299,23 +404,29 @@ class BlobMetadata { return null; } boolean isALeasee(@NonNull String packageName) { return isALeasee(packageName, INVALID_UID); boolean shouldAttributeToLeasee(@NonNull String packageName, int userId, boolean callerHasStatsPermission) { if (!isALeaseeInUser(packageName, INVALID_UID, userId)) { return false; } boolean isALeasee(int uid) { return isALeasee(null, uid); if (!callerHasStatsPermission || !hasOtherLeasees(packageName, INVALID_UID, userId)) { return true; } boolean hasOtherLeasees(@NonNull String packageName) { return hasOtherLeasees(packageName, INVALID_UID); return false; } boolean hasOtherLeasees(int uid) { return hasOtherLeasees(null, uid); boolean shouldAttributeToLeasee(int uid, boolean callerHasStatsPermission) { final int userId = UserHandle.getUserId(uid); if (!isALeaseeInUser(null, uid, userId)) { return false; } if (!callerHasStatsPermission || !hasOtherLeasees(null, uid, userId)) { return true; } return false; } private boolean hasOtherLeasees(@Nullable String packageName, int uid) { private boolean hasOtherLeasees(@Nullable String packageName, int uid, int userId) { synchronized (mMetadataLock) { for (int i = 0, size = mLeasees.size(); i < size; ++i) { final Leasee leasee = mLeasees.valueAt(i); Loading @@ -326,7 +437,8 @@ class BlobMetadata { if (packageName != null && uid != INVALID_UID && !leasee.equals(packageName, uid)) { return true; } else if (packageName != null && !leasee.packageName.equals(packageName)) { } else if (packageName != null && (!leasee.packageName.equals(packageName) || userId != UserHandle.getUserId(leasee.uid))) { return true; } else if (uid != INVALID_UID && leasee.uid != uid) { return true; Loading Loading @@ -371,7 +483,7 @@ class BlobMetadata { return mBlobFile; } ParcelFileDescriptor openForRead(String callingPackage) throws IOException { ParcelFileDescriptor openForRead(String callingPackage, int callingUid) throws IOException { // TODO: Add limit on opened fds FileDescriptor fd; try { Loading @@ -381,7 +493,7 @@ class BlobMetadata { } try { if (BlobStoreConfig.shouldUseRevocableFdForReads()) { return createRevocableFd(fd, callingPackage); return createRevocableFd(fd, callingPackage, callingUid); } else { return new ParcelFileDescriptor(fd); } Loading @@ -393,26 +505,28 @@ class BlobMetadata { @NonNull private ParcelFileDescriptor createRevocableFd(FileDescriptor fd, String callingPackage) throws IOException { String callingPackage, int callingUid) throws IOException { final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd); final Accessor accessor; synchronized (mRevocableFds) { ArraySet<RevocableFileDescriptor> revocableFdsForPkg = mRevocableFds.get(callingPackage); if (revocableFdsForPkg == null) { revocableFdsForPkg = new ArraySet<>(); mRevocableFds.put(callingPackage, revocableFdsForPkg); accessor = new Accessor(callingPackage, callingUid); ArraySet<RevocableFileDescriptor> revocableFdsForAccessor = mRevocableFds.get(accessor); if (revocableFdsForAccessor == null) { revocableFdsForAccessor = new ArraySet<>(); mRevocableFds.put(accessor, revocableFdsForAccessor); } revocableFdsForPkg.add(revocableFd); revocableFdsForAccessor.add(revocableFd); } revocableFd.addOnCloseListener((e) -> { synchronized (mRevocableFds) { final ArraySet<RevocableFileDescriptor> revocableFdsForPkg = mRevocableFds.get(callingPackage); if (revocableFdsForPkg != null) { revocableFdsForPkg.remove(revocableFd); if (revocableFdsForPkg.isEmpty()) { mRevocableFds.remove(callingPackage); final ArraySet<RevocableFileDescriptor> revocableFdsForAccessor = mRevocableFds.get(accessor); if (revocableFdsForAccessor != null) { revocableFdsForAccessor.remove(revocableFd); if (revocableFdsForAccessor.isEmpty()) { mRevocableFds.remove(accessor); } } } Loading @@ -421,22 +535,23 @@ class BlobMetadata { } void destroy() { revokeAllFds(); revokeAndClearAllFds(); getBlobFile().delete(); } private void revokeAllFds() { private void revokeAndClearAllFds() { synchronized (mRevocableFds) { for (int i = 0, pkgCount = mRevocableFds.size(); i < pkgCount; ++i) { final ArraySet<RevocableFileDescriptor> packageFds = for (int i = 0, accessorCount = mRevocableFds.size(); i < accessorCount; ++i) { final ArraySet<RevocableFileDescriptor> rFds = mRevocableFds.valueAt(i); if (packageFds == null) { if (rFds == null) { continue; } for (int j = 0, fdCount = packageFds.size(); j < fdCount; ++j) { packageFds.valueAt(j).revoke(); for (int j = 0, fdCount = rFds.size(); j < fdCount; ++j) { rFds.valueAt(j).revoke(); } } mRevocableFds.clear(); } } Loading Loading @@ -547,10 +662,10 @@ class BlobMetadata { fout.println("<empty>"); } else { for (int i = 0, count = mRevocableFds.size(); i < count; ++i) { final String packageName = mRevocableFds.keyAt(i); final ArraySet<RevocableFileDescriptor> packageFds = final Accessor accessor = mRevocableFds.keyAt(i); final ArraySet<RevocableFileDescriptor> rFds = mRevocableFds.valueAt(i); fout.println(packageName + "#" + packageFds.size()); fout.println(accessor + ": #" + rFds.size()); } } fout.decreaseIndent(); Loading @@ -560,7 +675,6 @@ class BlobMetadata { void writeToXml(XmlSerializer out) throws IOException { synchronized (mMetadataLock) { XmlUtils.writeLongAttribute(out, ATTR_ID, mBlobId); XmlUtils.writeIntAttribute(out, ATTR_USER_ID, mUserId); out.startTag(null, TAG_BLOB_HANDLE); mBlobHandle.writeToXml(out); Loading @@ -584,7 +698,9 @@ class BlobMetadata { static BlobMetadata createFromXml(XmlPullParser in, int version, Context context) throws XmlPullParserException, IOException { final long blobId = XmlUtils.readLongAttribute(in, ATTR_ID); final int userId = XmlUtils.readIntAttribute(in, ATTR_USER_ID); if (version < XML_VERSION_ALLOW_ACCESS_ACROSS_USERS) { XmlUtils.readIntAttribute(in, ATTR_USER_ID); } BlobHandle blobHandle = null; final ArraySet<Committer> committers = new ArraySet<>(); Loading @@ -608,7 +724,7 @@ class BlobMetadata { return null; } final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle, userId); final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle); blobMetadata.setCommitters(committers); blobMetadata.setLeasees(leasees); return blobMetadata; Loading
apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java +3 −3 Original line number Diff line number Diff line Loading @@ -27,12 +27,11 @@ import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; import android.text.TextUtils; import android.util.DataUnit; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import android.util.TimeUtils; import com.android.internal.util.IndentingPrintWriter; import java.io.File; import java.util.concurrent.TimeUnit; Loading @@ -47,8 +46,9 @@ class BlobStoreConfig { public static final int XML_VERSION_ADD_DESC_RES_NAME = 3; public static final int XML_VERSION_ADD_COMMIT_TIME = 4; public static final int XML_VERSION_ADD_SESSION_CREATION_TIME = 5; public static final int XML_VERSION_ALLOW_ACCESS_ACROSS_USERS = 6; public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_SESSION_CREATION_TIME; public static final int XML_VERSION_CURRENT = XML_VERSION_ALLOW_ACCESS_ACROSS_USERS; public static final long INVALID_BLOB_ID = 0; public static final long INVALID_BLOB_SIZE = 0; Loading