Allow certain apps to access blobs across users.
BlobStoreManagerService is updated to store only one copy of any blob on device and to allow apps with new ACCESS_BLOBS_ACROSS_USERS priv permission to access blobs commited by their instances on other users. Bug: 175844032 Test: atest --test-mapping apex/blobstore Change-Id: If6d10d90b17bde33baf9e83eeaae75d09d0b50ae
This commit is contained in:
parent
086a0be6b2
commit
523643c45c
@ -26,8 +26,8 @@ import android.annotation.NonNull;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.util.Base64;
|
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.Preconditions;
|
||||||
import com.android.internal.util.XmlUtils;
|
import com.android.internal.util.XmlUtils;
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@ import android.permission.PermissionManager;
|
|||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.DebugUtils;
|
import android.util.DebugUtils;
|
||||||
|
import android.util.IndentingPrintWriter;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
|
||||||
import com.android.internal.util.XmlUtils;
|
import com.android.internal.util.XmlUtils;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.server.blob;
|
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_COMMIT_TIME_MS;
|
||||||
import static android.app.blob.XmlTags.ATTR_DESCRIPTION;
|
import static android.app.blob.XmlTags.ATTR_DESCRIPTION;
|
||||||
import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME;
|
import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME;
|
||||||
@ -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_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_DESC_RES_NAME;
|
||||||
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC;
|
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.BlobStoreConfig.hasLeaseWaitTimeElapsed;
|
||||||
import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId;
|
import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId;
|
||||||
import static com.android.server.blob.BlobStoreUtils.getPackageResources;
|
import static com.android.server.blob.BlobStoreUtils.getPackageResources;
|
||||||
@ -45,15 +47,18 @@ import android.annotation.Nullable;
|
|||||||
import android.app.blob.BlobHandle;
|
import android.app.blob.BlobHandle;
|
||||||
import android.app.blob.LeaseInfo;
|
import android.app.blob.LeaseInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.ResourceId;
|
import android.content.res.ResourceId;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.RevocableFileDescriptor;
|
import android.os.RevocableFileDescriptor;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.permission.PermissionManager;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
|
import android.util.IndentingPrintWriter;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.util.StatsEvent;
|
import android.util.StatsEvent;
|
||||||
@ -62,7 +67,6 @@ import android.util.proto.ProtoOutputStream;
|
|||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.FrameworkStatsLog;
|
import com.android.internal.util.FrameworkStatsLog;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
|
||||||
import com.android.internal.util.XmlUtils;
|
import com.android.internal.util.XmlUtils;
|
||||||
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
|
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
|
||||||
|
|
||||||
@ -85,7 +89,6 @@ class BlobMetadata {
|
|||||||
|
|
||||||
private final long mBlobId;
|
private final long mBlobId;
|
||||||
private final BlobHandle mBlobHandle;
|
private final BlobHandle mBlobHandle;
|
||||||
private final int mUserId;
|
|
||||||
|
|
||||||
@GuardedBy("mMetadataLock")
|
@GuardedBy("mMetadataLock")
|
||||||
private final ArraySet<Committer> mCommitters = new ArraySet<>();
|
private final ArraySet<Committer> mCommitters = new ArraySet<>();
|
||||||
@ -94,24 +97,23 @@ class BlobMetadata {
|
|||||||
private final ArraySet<Leasee> mLeasees = new ArraySet<>();
|
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
|
* 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
|
* 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.
|
* do not have any reference to the blob and the space occupied by the blob can be freed.
|
||||||
*/
|
*/
|
||||||
@GuardedBy("mRevocableFds")
|
@GuardedBy("mRevocableFds")
|
||||||
private final ArrayMap<String, ArraySet<RevocableFileDescriptor>> mRevocableFds =
|
private final ArrayMap<Accessor, ArraySet<RevocableFileDescriptor>> mRevocableFds =
|
||||||
new ArrayMap<>();
|
new ArrayMap<>();
|
||||||
|
|
||||||
// Do not access this directly, instead use #getBlobFile().
|
// Do not access this directly, instead use #getBlobFile().
|
||||||
private File mBlobFile;
|
private File mBlobFile;
|
||||||
|
|
||||||
BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) {
|
BlobMetadata(Context context, long blobId, BlobHandle blobHandle) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
this.mBlobId = blobId;
|
this.mBlobId = blobId;
|
||||||
this.mBlobHandle = blobHandle;
|
this.mBlobHandle = blobHandle;
|
||||||
this.mUserId = userId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long getBlobId() {
|
long getBlobId() {
|
||||||
@ -122,10 +124,6 @@ class BlobMetadata {
|
|||||||
return mBlobHandle;
|
return mBlobHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getUserId() {
|
|
||||||
return mUserId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addOrReplaceCommitter(@NonNull Committer committer) {
|
void addOrReplaceCommitter(@NonNull Committer committer) {
|
||||||
synchronized (mMetadataLock) {
|
synchronized (mMetadataLock) {
|
||||||
// We need to override the committer data, so first remove any existing
|
// We need to override the committer data, so first remove any existing
|
||||||
@ -155,13 +153,24 @@ class BlobMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeCommittersFromUnknownPkgs(SparseArray<String> knownPackages) {
|
void removeCommittersFromUnknownPkgs(SparseArray<SparseArray<String>> knownPackages) {
|
||||||
synchronized (mMetadataLock) {
|
synchronized (mMetadataLock) {
|
||||||
mCommitters.removeIf(committer ->
|
mCommitters.removeIf(committer -> {
|
||||||
!committer.packageName.equals(knownPackages.get(committer.uid)));
|
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
|
@Nullable
|
||||||
Committer getExistingCommitter(@NonNull String packageName, int uid) {
|
Committer getExistingCommitter(@NonNull String packageName, int uid) {
|
||||||
synchronized (mCommitters) {
|
synchronized (mCommitters) {
|
||||||
@ -201,10 +210,16 @@ class BlobMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeLeaseesFromUnknownPkgs(SparseArray<String> knownPackages) {
|
void removeLeaseesFromUnknownPkgs(SparseArray<SparseArray<String>> knownPackages) {
|
||||||
synchronized (mMetadataLock) {
|
synchronized (mMetadataLock) {
|
||||||
mLeasees.removeIf(leasee ->
|
mLeasees.removeIf(leasee -> {
|
||||||
!leasee.packageName.equals(knownPackages.get(leasee.uid)));
|
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));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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() {
|
boolean hasValidLeases() {
|
||||||
synchronized (mMetadataLock) {
|
synchronized (mMetadataLock) {
|
||||||
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
|
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
|
||||||
@ -244,8 +278,12 @@ class BlobMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final int callingUserId = UserHandle.getUserId(callingUid);
|
||||||
for (int i = 0, size = mCommitters.size(); i < size; ++i) {
|
for (int i = 0, size = mCommitters.size(); i < size; ++i) {
|
||||||
final Committer committer = mCommitters.valueAt(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.
|
// Check if the caller is the same package that committed the blob.
|
||||||
if (committer.equals(callingPackage, callingUid)) {
|
if (committer.equals(callingPackage, callingUid)) {
|
||||||
@ -259,38 +297,105 @@ class BlobMetadata {
|
|||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isACommitter(@NonNull String packageName, int uid) {
|
boolean isACommitter(@NonNull String packageName, int uid) {
|
||||||
synchronized (mMetadataLock) {
|
synchronized (mMetadataLock) {
|
||||||
return isAnAccessor(mCommitters, packageName, uid);
|
return isAnAccessor(mCommitters, packageName, uid, UserHandle.getUserId(uid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isALeasee(@Nullable String packageName, int uid) {
|
boolean isALeasee(@Nullable String packageName, int uid) {
|
||||||
synchronized (mMetadataLock) {
|
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();
|
return leasee != null && leasee.isStillValid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T extends Accessor> boolean isAnAccessor(@NonNull ArraySet<T> accessors,
|
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.
|
// 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,
|
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.
|
// Check if the package is an accessor of the data blob.
|
||||||
for (int i = 0, size = accessors.size(); i < size; ++i) {
|
for (int i = 0, size = accessors.size(); i < size; ++i) {
|
||||||
final Accessor accessor = accessors.valueAt(i);
|
final Accessor accessor = accessors.valueAt(i);
|
||||||
if (packageName != null && uid != INVALID_UID
|
if (packageName != null && uid != INVALID_UID
|
||||||
&& accessor.equals(packageName, uid)) {
|
&& accessor.equals(packageName, uid)) {
|
||||||
return (T) accessor;
|
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;
|
return (T) accessor;
|
||||||
} else if (uid != INVALID_UID && accessor.uid == uid) {
|
} else if (uid != INVALID_UID && accessor.uid == uid) {
|
||||||
return (T) accessor;
|
return (T) accessor;
|
||||||
@ -299,23 +404,29 @@ class BlobMetadata {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isALeasee(@NonNull String packageName) {
|
boolean shouldAttributeToLeasee(@NonNull String packageName, int userId,
|
||||||
return isALeasee(packageName, INVALID_UID);
|
boolean callerHasStatsPermission) {
|
||||||
|
if (!isALeaseeInUser(packageName, INVALID_UID, userId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!callerHasStatsPermission || !hasOtherLeasees(packageName, INVALID_UID, userId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isALeasee(int uid) {
|
boolean shouldAttributeToLeasee(int uid, boolean callerHasStatsPermission) {
|
||||||
return isALeasee(null, uid);
|
final int userId = UserHandle.getUserId(uid);
|
||||||
|
if (!isALeaseeInUser(null, uid, userId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!callerHasStatsPermission || !hasOtherLeasees(null, uid, userId)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasOtherLeasees(@NonNull String packageName) {
|
private boolean hasOtherLeasees(@Nullable String packageName, int uid, int userId) {
|
||||||
return hasOtherLeasees(packageName, INVALID_UID);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasOtherLeasees(int uid) {
|
|
||||||
return hasOtherLeasees(null, uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasOtherLeasees(@Nullable String packageName, int uid) {
|
|
||||||
synchronized (mMetadataLock) {
|
synchronized (mMetadataLock) {
|
||||||
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
|
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
|
||||||
final Leasee leasee = mLeasees.valueAt(i);
|
final Leasee leasee = mLeasees.valueAt(i);
|
||||||
@ -326,7 +437,8 @@ class BlobMetadata {
|
|||||||
if (packageName != null && uid != INVALID_UID
|
if (packageName != null && uid != INVALID_UID
|
||||||
&& !leasee.equals(packageName, uid)) {
|
&& !leasee.equals(packageName, uid)) {
|
||||||
return true;
|
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;
|
return true;
|
||||||
} else if (uid != INVALID_UID && leasee.uid != uid) {
|
} else if (uid != INVALID_UID && leasee.uid != uid) {
|
||||||
return true;
|
return true;
|
||||||
@ -371,7 +483,7 @@ class BlobMetadata {
|
|||||||
return mBlobFile;
|
return mBlobFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
ParcelFileDescriptor openForRead(String callingPackage) throws IOException {
|
ParcelFileDescriptor openForRead(String callingPackage, int callingUid) throws IOException {
|
||||||
// TODO: Add limit on opened fds
|
// TODO: Add limit on opened fds
|
||||||
FileDescriptor fd;
|
FileDescriptor fd;
|
||||||
try {
|
try {
|
||||||
@ -381,7 +493,7 @@ class BlobMetadata {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (BlobStoreConfig.shouldUseRevocableFdForReads()) {
|
if (BlobStoreConfig.shouldUseRevocableFdForReads()) {
|
||||||
return createRevocableFd(fd, callingPackage);
|
return createRevocableFd(fd, callingPackage, callingUid);
|
||||||
} else {
|
} else {
|
||||||
return new ParcelFileDescriptor(fd);
|
return new ParcelFileDescriptor(fd);
|
||||||
}
|
}
|
||||||
@ -393,26 +505,28 @@ class BlobMetadata {
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private ParcelFileDescriptor createRevocableFd(FileDescriptor fd,
|
private ParcelFileDescriptor createRevocableFd(FileDescriptor fd,
|
||||||
String callingPackage) throws IOException {
|
String callingPackage, int callingUid) throws IOException {
|
||||||
final RevocableFileDescriptor revocableFd =
|
final RevocableFileDescriptor revocableFd =
|
||||||
new RevocableFileDescriptor(mContext, fd);
|
new RevocableFileDescriptor(mContext, fd);
|
||||||
|
final Accessor accessor;
|
||||||
synchronized (mRevocableFds) {
|
synchronized (mRevocableFds) {
|
||||||
ArraySet<RevocableFileDescriptor> revocableFdsForPkg =
|
accessor = new Accessor(callingPackage, callingUid);
|
||||||
mRevocableFds.get(callingPackage);
|
ArraySet<RevocableFileDescriptor> revocableFdsForAccessor =
|
||||||
if (revocableFdsForPkg == null) {
|
mRevocableFds.get(accessor);
|
||||||
revocableFdsForPkg = new ArraySet<>();
|
if (revocableFdsForAccessor == null) {
|
||||||
mRevocableFds.put(callingPackage, revocableFdsForPkg);
|
revocableFdsForAccessor = new ArraySet<>();
|
||||||
|
mRevocableFds.put(accessor, revocableFdsForAccessor);
|
||||||
}
|
}
|
||||||
revocableFdsForPkg.add(revocableFd);
|
revocableFdsForAccessor.add(revocableFd);
|
||||||
}
|
}
|
||||||
revocableFd.addOnCloseListener((e) -> {
|
revocableFd.addOnCloseListener((e) -> {
|
||||||
synchronized (mRevocableFds) {
|
synchronized (mRevocableFds) {
|
||||||
final ArraySet<RevocableFileDescriptor> revocableFdsForPkg =
|
final ArraySet<RevocableFileDescriptor> revocableFdsForAccessor =
|
||||||
mRevocableFds.get(callingPackage);
|
mRevocableFds.get(accessor);
|
||||||
if (revocableFdsForPkg != null) {
|
if (revocableFdsForAccessor != null) {
|
||||||
revocableFdsForPkg.remove(revocableFd);
|
revocableFdsForAccessor.remove(revocableFd);
|
||||||
if (revocableFdsForPkg.isEmpty()) {
|
if (revocableFdsForAccessor.isEmpty()) {
|
||||||
mRevocableFds.remove(callingPackage);
|
mRevocableFds.remove(accessor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,22 +535,23 @@ class BlobMetadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void destroy() {
|
void destroy() {
|
||||||
revokeAllFds();
|
revokeAndClearAllFds();
|
||||||
getBlobFile().delete();
|
getBlobFile().delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void revokeAllFds() {
|
private void revokeAndClearAllFds() {
|
||||||
synchronized (mRevocableFds) {
|
synchronized (mRevocableFds) {
|
||||||
for (int i = 0, pkgCount = mRevocableFds.size(); i < pkgCount; ++i) {
|
for (int i = 0, accessorCount = mRevocableFds.size(); i < accessorCount; ++i) {
|
||||||
final ArraySet<RevocableFileDescriptor> packageFds =
|
final ArraySet<RevocableFileDescriptor> rFds =
|
||||||
mRevocableFds.valueAt(i);
|
mRevocableFds.valueAt(i);
|
||||||
if (packageFds == null) {
|
if (rFds == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (int j = 0, fdCount = packageFds.size(); j < fdCount; ++j) {
|
for (int j = 0, fdCount = rFds.size(); j < fdCount; ++j) {
|
||||||
packageFds.valueAt(j).revoke();
|
rFds.valueAt(j).revoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mRevocableFds.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,10 +662,10 @@ class BlobMetadata {
|
|||||||
fout.println("<empty>");
|
fout.println("<empty>");
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0, count = mRevocableFds.size(); i < count; ++i) {
|
for (int i = 0, count = mRevocableFds.size(); i < count; ++i) {
|
||||||
final String packageName = mRevocableFds.keyAt(i);
|
final Accessor accessor = mRevocableFds.keyAt(i);
|
||||||
final ArraySet<RevocableFileDescriptor> packageFds =
|
final ArraySet<RevocableFileDescriptor> rFds =
|
||||||
mRevocableFds.valueAt(i);
|
mRevocableFds.valueAt(i);
|
||||||
fout.println(packageName + "#" + packageFds.size());
|
fout.println(accessor + ": #" + rFds.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fout.decreaseIndent();
|
fout.decreaseIndent();
|
||||||
@ -560,7 +675,6 @@ class BlobMetadata {
|
|||||||
void writeToXml(XmlSerializer out) throws IOException {
|
void writeToXml(XmlSerializer out) throws IOException {
|
||||||
synchronized (mMetadataLock) {
|
synchronized (mMetadataLock) {
|
||||||
XmlUtils.writeLongAttribute(out, ATTR_ID, mBlobId);
|
XmlUtils.writeLongAttribute(out, ATTR_ID, mBlobId);
|
||||||
XmlUtils.writeIntAttribute(out, ATTR_USER_ID, mUserId);
|
|
||||||
|
|
||||||
out.startTag(null, TAG_BLOB_HANDLE);
|
out.startTag(null, TAG_BLOB_HANDLE);
|
||||||
mBlobHandle.writeToXml(out);
|
mBlobHandle.writeToXml(out);
|
||||||
@ -584,7 +698,9 @@ class BlobMetadata {
|
|||||||
static BlobMetadata createFromXml(XmlPullParser in, int version, Context context)
|
static BlobMetadata createFromXml(XmlPullParser in, int version, Context context)
|
||||||
throws XmlPullParserException, IOException {
|
throws XmlPullParserException, IOException {
|
||||||
final long blobId = XmlUtils.readLongAttribute(in, ATTR_ID);
|
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;
|
BlobHandle blobHandle = null;
|
||||||
final ArraySet<Committer> committers = new ArraySet<>();
|
final ArraySet<Committer> committers = new ArraySet<>();
|
||||||
@ -608,7 +724,7 @@ class BlobMetadata {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle, userId);
|
final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle);
|
||||||
blobMetadata.setCommitters(committers);
|
blobMetadata.setCommitters(committers);
|
||||||
blobMetadata.setLeasees(leasees);
|
blobMetadata.setLeasees(leasees);
|
||||||
return blobMetadata;
|
return blobMetadata;
|
||||||
|
@ -27,12 +27,11 @@ import android.provider.DeviceConfig;
|
|||||||
import android.provider.DeviceConfig.Properties;
|
import android.provider.DeviceConfig.Properties;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.DataUnit;
|
import android.util.DataUnit;
|
||||||
|
import android.util.IndentingPrintWriter;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.TimeUtils;
|
import android.util.TimeUtils;
|
||||||
|
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@ -47,8 +46,9 @@ class BlobStoreConfig {
|
|||||||
public static final int XML_VERSION_ADD_DESC_RES_NAME = 3;
|
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_COMMIT_TIME = 4;
|
||||||
public static final int XML_VERSION_ADD_SESSION_CREATION_TIME = 5;
|
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_ID = 0;
|
||||||
public static final long INVALID_BLOB_SIZE = 0;
|
public static final long INVALID_BLOB_SIZE = 0;
|
||||||
|
@ -32,6 +32,7 @@ import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_ID;
|
|||||||
import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_SIZE;
|
import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_SIZE;
|
||||||
import static com.android.server.blob.BlobStoreConfig.LOGV;
|
import static com.android.server.blob.BlobStoreConfig.LOGV;
|
||||||
import static com.android.server.blob.BlobStoreConfig.TAG;
|
import static com.android.server.blob.BlobStoreConfig.TAG;
|
||||||
|
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ALLOW_ACCESS_ACROSS_USERS;
|
||||||
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
|
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.getAdjustedCommitTimeMs;
|
||||||
import static com.android.server.blob.BlobStoreConfig.getDeletionOnLastLeaseDelayMs;
|
import static com.android.server.blob.BlobStoreConfig.getDeletionOnLastLeaseDelayMs;
|
||||||
@ -83,6 +84,7 @@ import android.util.ArrayMap;
|
|||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.AtomicFile;
|
import android.util.AtomicFile;
|
||||||
import android.util.ExceptionUtils;
|
import android.util.ExceptionUtils;
|
||||||
|
import android.util.IndentingPrintWriter;
|
||||||
import android.util.LongSparseArray;
|
import android.util.LongSparseArray;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
@ -96,7 +98,6 @@ import com.android.internal.util.CollectionUtils;
|
|||||||
import com.android.internal.util.DumpUtils;
|
import com.android.internal.util.DumpUtils;
|
||||||
import com.android.internal.util.FastXmlSerializer;
|
import com.android.internal.util.FastXmlSerializer;
|
||||||
import com.android.internal.util.FrameworkStatsLog;
|
import com.android.internal.util.FrameworkStatsLog;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
|
||||||
import com.android.internal.util.Preconditions;
|
import com.android.internal.util.Preconditions;
|
||||||
import com.android.internal.util.XmlUtils;
|
import com.android.internal.util.XmlUtils;
|
||||||
import com.android.internal.util.function.pooled.PooledLambda;
|
import com.android.internal.util.function.pooled.PooledLambda;
|
||||||
@ -129,6 +130,7 @@ import java.util.Random;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@ -146,9 +148,9 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
@GuardedBy("mBlobsLock")
|
@GuardedBy("mBlobsLock")
|
||||||
private long mCurrentMaxSessionId;
|
private long mCurrentMaxSessionId;
|
||||||
|
|
||||||
// Contains data of userId -> {BlobHandle -> {BlobMetadata}}
|
// Contains data of BlobHandle -> BlobMetadata.
|
||||||
@GuardedBy("mBlobsLock")
|
@GuardedBy("mBlobsLock")
|
||||||
private final SparseArray<ArrayMap<BlobHandle, BlobMetadata>> mBlobsMap = new SparseArray<>();
|
private final ArrayMap<BlobHandle, BlobMetadata> mBlobsMap = new ArrayMap<>();
|
||||||
|
|
||||||
// Contains all ids that are currently in use.
|
// Contains all ids that are currently in use.
|
||||||
@GuardedBy("mBlobsLock")
|
@GuardedBy("mBlobsLock")
|
||||||
@ -265,16 +267,6 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
return userSessions;
|
return userSessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mBlobsLock")
|
|
||||||
private ArrayMap<BlobHandle, BlobMetadata> getUserBlobsLocked(int userId) {
|
|
||||||
ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.get(userId);
|
|
||||||
if (userBlobs == null) {
|
|
||||||
userBlobs = new ArrayMap<>();
|
|
||||||
mBlobsMap.put(userId, userBlobs);
|
|
||||||
}
|
|
||||||
return userBlobs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void addUserSessionsForTest(LongSparseArray<BlobStoreSession> userSessions, int userId) {
|
void addUserSessionsForTest(LongSparseArray<BlobStoreSession> userSessions, int userId) {
|
||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsLock) {
|
||||||
@ -283,9 +275,16 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void addUserBlobsForTest(ArrayMap<BlobHandle, BlobMetadata> userBlobs, int userId) {
|
BlobMetadata getBlobForTest(BlobHandle blobHandle) {
|
||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsLock) {
|
||||||
mBlobsMap.put(userId, userBlobs);
|
return mBlobsMap.get(blobHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
int getBlobsCountForTest() {
|
||||||
|
synchronized (mBlobsLock) {
|
||||||
|
return mBlobsMap.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,14 +318,9 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mBlobsLock")
|
@GuardedBy("mBlobsLock")
|
||||||
private void addBlobForUserLocked(BlobMetadata blobMetadata, int userId) {
|
@VisibleForTesting
|
||||||
addBlobForUserLocked(blobMetadata, getUserBlobsLocked(userId));
|
void addBlobLocked(BlobMetadata blobMetadata) {
|
||||||
}
|
mBlobsMap.put(blobMetadata.getBlobHandle(), blobMetadata);
|
||||||
|
|
||||||
@GuardedBy("mBlobsLock")
|
|
||||||
private void addBlobForUserLocked(BlobMetadata blobMetadata,
|
|
||||||
ArrayMap<BlobHandle, BlobMetadata> userBlobs) {
|
|
||||||
userBlobs.put(blobMetadata.getBlobHandle(), blobMetadata);
|
|
||||||
addActiveBlobIdLocked(blobMetadata.getBlobId());
|
addActiveBlobIdLocked(blobMetadata.getBlobId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,8 +398,7 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
private ParcelFileDescriptor openBlobInternal(BlobHandle blobHandle, int callingUid,
|
private ParcelFileDescriptor openBlobInternal(BlobHandle blobHandle, int callingUid,
|
||||||
String callingPackage, String attributionTag) throws IOException {
|
String callingPackage, String attributionTag) throws IOException {
|
||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsLock) {
|
||||||
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
|
final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
|
||||||
.get(blobHandle);
|
|
||||||
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
|
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
|
||||||
callingPackage, callingUid, attributionTag)) {
|
callingPackage, callingUid, attributionTag)) {
|
||||||
if (blobMetadata == null) {
|
if (blobMetadata == null) {
|
||||||
@ -415,7 +408,7 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
} else {
|
} else {
|
||||||
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
|
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
|
||||||
blobMetadata.getBlobId(), blobMetadata.getSize(),
|
blobMetadata.getBlobId(), blobMetadata.getSize(),
|
||||||
FrameworkStatsLog.BLOB_LEASED__RESULT__ACCESS_NOT_ALLOWED);
|
FrameworkStatsLog.BLOB_OPENED__RESULT__ACCESS_NOT_ALLOWED);
|
||||||
}
|
}
|
||||||
throw new SecurityException("Caller not allowed to access " + blobHandle
|
throw new SecurityException("Caller not allowed to access " + blobHandle
|
||||||
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
|
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
|
||||||
@ -425,7 +418,7 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
blobMetadata.getBlobId(), blobMetadata.getSize(),
|
blobMetadata.getBlobId(), blobMetadata.getSize(),
|
||||||
FrameworkStatsLog.BLOB_OPENED__RESULT__SUCCESS);
|
FrameworkStatsLog.BLOB_OPENED__RESULT__SUCCESS);
|
||||||
|
|
||||||
return blobMetadata.openForRead(callingPackage);
|
return blobMetadata.openForRead(callingPackage, callingUid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,11 +426,11 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
private int getCommittedBlobsCountLocked(int uid, String packageName) {
|
private int getCommittedBlobsCountLocked(int uid, String packageName) {
|
||||||
// TODO: Maintain a counter instead of traversing all the blobs
|
// TODO: Maintain a counter instead of traversing all the blobs
|
||||||
final AtomicInteger blobsCount = new AtomicInteger(0);
|
final AtomicInteger blobsCount = new AtomicInteger(0);
|
||||||
forEachBlobInUser((blobMetadata) -> {
|
forEachBlobLocked(blobMetadata -> {
|
||||||
if (blobMetadata.isACommitter(packageName, uid)) {
|
if (blobMetadata.isACommitter(packageName, uid)) {
|
||||||
blobsCount.getAndIncrement();
|
blobsCount.getAndIncrement();
|
||||||
}
|
}
|
||||||
}, UserHandle.getUserId(uid));
|
});
|
||||||
return blobsCount.get();
|
return blobsCount.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,11 +438,11 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
private int getLeasedBlobsCountLocked(int uid, String packageName) {
|
private int getLeasedBlobsCountLocked(int uid, String packageName) {
|
||||||
// TODO: Maintain a counter instead of traversing all the blobs
|
// TODO: Maintain a counter instead of traversing all the blobs
|
||||||
final AtomicInteger blobsCount = new AtomicInteger(0);
|
final AtomicInteger blobsCount = new AtomicInteger(0);
|
||||||
forEachBlobInUser((blobMetadata) -> {
|
forEachBlobLocked(blobMetadata -> {
|
||||||
if (blobMetadata.isALeasee(packageName, uid)) {
|
if (blobMetadata.isALeasee(packageName, uid)) {
|
||||||
blobsCount.getAndIncrement();
|
blobsCount.getAndIncrement();
|
||||||
}
|
}
|
||||||
}, UserHandle.getUserId(uid));
|
});
|
||||||
return blobsCount.get();
|
return blobsCount.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,8 +458,16 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
throw new LimitExceededException("Too many leased blobs for the caller: "
|
throw new LimitExceededException("Too many leased blobs for the caller: "
|
||||||
+ leasesCount);
|
+ leasesCount);
|
||||||
}
|
}
|
||||||
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
|
if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0
|
||||||
.get(blobHandle);
|
&& leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
|
||||||
|
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
|
||||||
|
INVALID_BLOB_ID, INVALID_BLOB_SIZE,
|
||||||
|
FrameworkStatsLog.BLOB_LEASED__RESULT__LEASE_EXPIRY_INVALID);
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Lease expiry cannot be later than blobs expiry time");
|
||||||
|
}
|
||||||
|
|
||||||
|
final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
|
||||||
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
|
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
|
||||||
callingPackage, callingUid, attributionTag)) {
|
callingPackage, callingUid, attributionTag)) {
|
||||||
if (blobMetadata == null) {
|
if (blobMetadata == null) {
|
||||||
@ -481,15 +482,7 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
throw new SecurityException("Caller not allowed to access " + blobHandle
|
throw new SecurityException("Caller not allowed to access " + blobHandle
|
||||||
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
|
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
|
||||||
}
|
}
|
||||||
if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0
|
|
||||||
&& leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
|
|
||||||
|
|
||||||
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
|
|
||||||
blobMetadata.getBlobId(), blobMetadata.getSize(),
|
|
||||||
FrameworkStatsLog.BLOB_LEASED__RESULT__LEASE_EXPIRY_INVALID);
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Lease expiry cannot be later than blobs expiry time");
|
|
||||||
}
|
|
||||||
if (blobMetadata.getSize()
|
if (blobMetadata.getSize()
|
||||||
> getRemainingLeaseQuotaBytesInternal(callingUid, callingPackage)) {
|
> getRemainingLeaseQuotaBytesInternal(callingUid, callingPackage)) {
|
||||||
|
|
||||||
@ -518,20 +511,18 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
@GuardedBy("mBlobsLock")
|
@GuardedBy("mBlobsLock")
|
||||||
long getTotalUsageBytesLocked(int callingUid, String callingPackage) {
|
long getTotalUsageBytesLocked(int callingUid, String callingPackage) {
|
||||||
final AtomicLong totalBytes = new AtomicLong(0);
|
final AtomicLong totalBytes = new AtomicLong(0);
|
||||||
forEachBlobInUser((blobMetadata) -> {
|
forEachBlobLocked((blobMetadata) -> {
|
||||||
if (blobMetadata.isALeasee(callingPackage, callingUid)) {
|
if (blobMetadata.isALeasee(callingPackage, callingUid)) {
|
||||||
totalBytes.getAndAdd(blobMetadata.getSize());
|
totalBytes.getAndAdd(blobMetadata.getSize());
|
||||||
}
|
}
|
||||||
}, UserHandle.getUserId(callingUid));
|
});
|
||||||
return totalBytes.get();
|
return totalBytes.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releaseLeaseInternal(BlobHandle blobHandle, int callingUid,
|
private void releaseLeaseInternal(BlobHandle blobHandle, int callingUid,
|
||||||
String callingPackage, String attributionTag) {
|
String callingPackage, String attributionTag) {
|
||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsLock) {
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
|
final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
|
||||||
getUserBlobsLocked(UserHandle.getUserId(callingUid));
|
|
||||||
final BlobMetadata blobMetadata = userBlobs.get(blobHandle);
|
|
||||||
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
|
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
|
||||||
callingPackage, callingUid, attributionTag)) {
|
callingPackage, callingUid, attributionTag)) {
|
||||||
throw new SecurityException("Caller not allowed to access " + blobHandle
|
throw new SecurityException("Caller not allowed to access " + blobHandle
|
||||||
@ -547,12 +538,12 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsLock) {
|
||||||
// Check if blobMetadata object is still valid. If it is not, then
|
// Check if blobMetadata object is still valid. If it is not, then
|
||||||
// it means that it was already deleted and nothing else to do here.
|
// it means that it was already deleted and nothing else to do here.
|
||||||
if (!Objects.equals(userBlobs.get(blobHandle), blobMetadata)) {
|
if (!Objects.equals(mBlobsMap.get(blobHandle), blobMetadata)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
|
if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
|
||||||
deleteBlobLocked(blobMetadata);
|
deleteBlobLocked(blobMetadata);
|
||||||
userBlobs.remove(blobHandle);
|
mBlobsMap.remove(blobHandle);
|
||||||
}
|
}
|
||||||
writeBlobsInfoAsync();
|
writeBlobsInfoAsync();
|
||||||
}
|
}
|
||||||
@ -583,12 +574,18 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
return packageResources;
|
return packageResources;
|
||||||
};
|
};
|
||||||
getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> {
|
forEachBlobLocked((blobHandle, blobMetadata) -> {
|
||||||
|
if (!blobMetadata.hasACommitterOrLeaseeInUser(userId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final ArrayList<LeaseInfo> leaseInfos = new ArrayList<>();
|
final ArrayList<LeaseInfo> leaseInfos = new ArrayList<>();
|
||||||
blobMetadata.forEachLeasee(leasee -> {
|
blobMetadata.forEachLeasee(leasee -> {
|
||||||
if (!leasee.isStillValid()) {
|
if (!leasee.isStillValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (userId != UserHandle.getUserId(leasee.uid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final int descriptionResId = leasee.descriptionResEntryName == null
|
final int descriptionResId = leasee.descriptionResEntryName == null
|
||||||
? Resources.ID_NULL
|
? Resources.ID_NULL
|
||||||
: getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
|
: getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
|
||||||
@ -608,9 +605,7 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
|
|
||||||
private void deleteBlobInternal(long blobId, int callingUid) {
|
private void deleteBlobInternal(long blobId, int callingUid) {
|
||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsLock) {
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
|
mBlobsMap.entrySet().removeIf(entry -> {
|
||||||
UserHandle.getUserId(callingUid));
|
|
||||||
userBlobs.entrySet().removeIf(entry -> {
|
|
||||||
final BlobMetadata blobMetadata = entry.getValue();
|
final BlobMetadata blobMetadata = entry.getValue();
|
||||||
if (blobMetadata.getBlobId() == blobId) {
|
if (blobMetadata.getBlobId() == blobId) {
|
||||||
deleteBlobLocked(blobMetadata);
|
deleteBlobLocked(blobMetadata);
|
||||||
@ -625,19 +620,20 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
private List<BlobHandle> getLeasedBlobsInternal(int callingUid,
|
private List<BlobHandle> getLeasedBlobsInternal(int callingUid,
|
||||||
@NonNull String callingPackage) {
|
@NonNull String callingPackage) {
|
||||||
final ArrayList<BlobHandle> leasedBlobs = new ArrayList<>();
|
final ArrayList<BlobHandle> leasedBlobs = new ArrayList<>();
|
||||||
forEachBlobInUser(blobMetadata -> {
|
synchronized (mBlobsLock) {
|
||||||
if (blobMetadata.isALeasee(callingPackage, callingUid)) {
|
forEachBlobLocked(blobMetadata -> {
|
||||||
leasedBlobs.add(blobMetadata.getBlobHandle());
|
if (blobMetadata.isALeasee(callingPackage, callingUid)) {
|
||||||
}
|
leasedBlobs.add(blobMetadata.getBlobHandle());
|
||||||
}, UserHandle.getUserId(callingUid));
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return leasedBlobs;
|
return leasedBlobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LeaseInfo getLeaseInfoInternal(BlobHandle blobHandle,
|
private LeaseInfo getLeaseInfoInternal(BlobHandle blobHandle,
|
||||||
int callingUid, @NonNull String callingPackage, String attributionTag) {
|
int callingUid, @NonNull String callingPackage, String attributionTag) {
|
||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsLock) {
|
||||||
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
|
final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
|
||||||
.get(blobHandle);
|
|
||||||
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
|
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
|
||||||
callingPackage, callingUid, attributionTag)) {
|
callingPackage, callingUid, attributionTag)) {
|
||||||
throw new SecurityException("Caller not allowed to access " + blobHandle
|
throw new SecurityException("Caller not allowed to access " + blobHandle
|
||||||
@ -699,14 +695,14 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
FrameworkStatsLog.BLOB_COMMITTED__RESULT__COUNT_LIMIT_EXCEEDED);
|
FrameworkStatsLog.BLOB_COMMITTED__RESULT__COUNT_LIMIT_EXCEEDED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
final int userId = UserHandle.getUserId(session.getOwnerUid());
|
final BlobMetadata blob;
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
|
final int blobIndex = mBlobsMap.indexOfKey(session.getBlobHandle());
|
||||||
userId);
|
if (blobIndex >= 0) {
|
||||||
BlobMetadata blob = userBlobs.get(session.getBlobHandle());
|
blob = mBlobsMap.valueAt(blobIndex);
|
||||||
if (blob == null) {
|
} else {
|
||||||
blob = new BlobMetadata(mContext, session.getSessionId(),
|
blob = new BlobMetadata(mContext, session.getSessionId(),
|
||||||
session.getBlobHandle(), userId);
|
session.getBlobHandle());
|
||||||
addBlobForUserLocked(blob, userBlobs);
|
addBlobLocked(blob);
|
||||||
}
|
}
|
||||||
final Committer existingCommitter = blob.getExistingCommitter(
|
final Committer existingCommitter = blob.getExistingCommitter(
|
||||||
session.getOwnerPackageName(), session.getOwnerUid());
|
session.getOwnerPackageName(), session.getOwnerUid());
|
||||||
@ -738,7 +734,7 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
// But if it is a recommit, just leave it as is.
|
// But if it is a recommit, just leave it as is.
|
||||||
if (session.getSessionId() == blob.getBlobId()) {
|
if (session.getSessionId() == blob.getBlobId()) {
|
||||||
deleteBlobLocked(blob);
|
deleteBlobLocked(blob);
|
||||||
userBlobs.remove(blob.getBlobHandle());
|
mBlobsMap.remove(blob.getBlobHandle());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Delete redundant data from recommits.
|
// Delete redundant data from recommits.
|
||||||
@ -874,13 +870,10 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
out.startTag(null, TAG_BLOBS);
|
out.startTag(null, TAG_BLOBS);
|
||||||
XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
|
XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
|
||||||
|
|
||||||
for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
|
for (int i = 0, count = mBlobsMap.size(); i < count; ++i) {
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
|
out.startTag(null, TAG_BLOB);
|
||||||
for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
|
mBlobsMap.valueAt(i).writeToXml(out);
|
||||||
out.startTag(null, TAG_BLOB);
|
out.endTag(null, TAG_BLOB);
|
||||||
userBlobs.valueAt(j).writeToXml(out);
|
|
||||||
out.endTag(null, TAG_BLOB);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.endTag(null, TAG_BLOBS);
|
out.endTag(null, TAG_BLOBS);
|
||||||
@ -925,16 +918,21 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
if (TAG_BLOB.equals(in.getName())) {
|
if (TAG_BLOB.equals(in.getName())) {
|
||||||
final BlobMetadata blobMetadata = BlobMetadata.createFromXml(
|
final BlobMetadata blobMetadata = BlobMetadata.createFromXml(
|
||||||
in, version, mContext);
|
in, version, mContext);
|
||||||
final SparseArray<String> userPackages = allPackages.get(
|
blobMetadata.removeCommittersFromUnknownPkgs(allPackages);
|
||||||
blobMetadata.getUserId());
|
blobMetadata.removeLeaseesFromUnknownPkgs(allPackages);
|
||||||
if (userPackages == null) {
|
|
||||||
blobMetadata.getBlobFile().delete();
|
|
||||||
} else {
|
|
||||||
addBlobForUserLocked(blobMetadata, blobMetadata.getUserId());
|
|
||||||
blobMetadata.removeCommittersFromUnknownPkgs(userPackages);
|
|
||||||
blobMetadata.removeLeaseesFromUnknownPkgs(userPackages);
|
|
||||||
}
|
|
||||||
mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId());
|
mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId());
|
||||||
|
if (version >= XML_VERSION_ALLOW_ACCESS_ACROSS_USERS) {
|
||||||
|
addBlobLocked(blobMetadata);
|
||||||
|
} else {
|
||||||
|
final BlobMetadata existingBlobMetadata = mBlobsMap.get(
|
||||||
|
blobMetadata.getBlobHandle());
|
||||||
|
if (existingBlobMetadata == null) {
|
||||||
|
addBlobLocked(blobMetadata);
|
||||||
|
} else {
|
||||||
|
existingBlobMetadata.addCommittersAndLeasees(blobMetadata);
|
||||||
|
blobMetadata.getBlobFile().delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (LOGV) {
|
if (LOGV) {
|
||||||
@ -977,14 +975,6 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getPackageUid(String packageName, int userId) {
|
|
||||||
final int uid = mPackageManagerInternal.getPackageUid(
|
|
||||||
packageName,
|
|
||||||
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES,
|
|
||||||
userId);
|
|
||||||
return uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SparseArray<SparseArray<String>> getAllPackages() {
|
private SparseArray<SparseArray<String>> getAllPackages() {
|
||||||
final SparseArray<SparseArray<String>> allPackages = new SparseArray<>();
|
final SparseArray<SparseArray<String>> allPackages = new SparseArray<>();
|
||||||
final int[] allUsers = LocalServices.getService(UserManagerInternal.class).getUserIds();
|
final int[] allUsers = LocalServices.getService(UserManagerInternal.class).getUserIds();
|
||||||
@ -1004,7 +994,7 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
return allPackages;
|
return allPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
AtomicFile prepareSessionsIndexFile() {
|
private AtomicFile prepareSessionsIndexFile() {
|
||||||
final File file = BlobStoreConfig.prepareSessionIndexFile();
|
final File file = BlobStoreConfig.prepareSessionIndexFile();
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -1012,7 +1002,7 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
return new AtomicFile(file, "session_index" /* commitLogTag */);
|
return new AtomicFile(file, "session_index" /* commitLogTag */);
|
||||||
}
|
}
|
||||||
|
|
||||||
AtomicFile prepareBlobsIndexFile() {
|
private AtomicFile prepareBlobsIndexFile() {
|
||||||
final File file = BlobStoreConfig.prepareBlobsIndexFile();
|
final File file = BlobStoreConfig.prepareBlobsIndexFile();
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -1037,9 +1027,7 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
writeBlobSessionsAsync();
|
writeBlobSessionsAsync();
|
||||||
|
|
||||||
// Remove the package from the committer and leasee list
|
// Remove the package from the committer and leasee list
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
|
mBlobsMap.entrySet().removeIf(entry -> {
|
||||||
getUserBlobsLocked(UserHandle.getUserId(uid));
|
|
||||||
userBlobs.entrySet().removeIf(entry -> {
|
|
||||||
final BlobMetadata blobMetadata = entry.getValue();
|
final BlobMetadata blobMetadata = entry.getValue();
|
||||||
final boolean isACommitter = blobMetadata.isACommitter(packageName, uid);
|
final boolean isACommitter = blobMetadata.isACommitter(packageName, uid);
|
||||||
if (isACommitter) {
|
if (isACommitter) {
|
||||||
@ -1074,14 +1062,15 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
|
mBlobsMap.entrySet().removeIf(entry -> {
|
||||||
mBlobsMap.removeReturnOld(userId);
|
final BlobMetadata blobMetadata = entry.getValue();
|
||||||
if (userBlobs != null) {
|
blobMetadata.removeDataForUser(userId);
|
||||||
for (int i = 0, count = userBlobs.size(); i < count; ++i) {
|
if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
|
||||||
final BlobMetadata blobMetadata = userBlobs.valueAt(i);
|
|
||||||
deleteBlobLocked(blobMetadata);
|
deleteBlobLocked(blobMetadata);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
|
});
|
||||||
if (LOGV) {
|
if (LOGV) {
|
||||||
Slog.v(TAG, "Removed blobs data in user " + userId);
|
Slog.v(TAG, "Removed blobs data in user " + userId);
|
||||||
}
|
}
|
||||||
@ -1114,22 +1103,19 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup any stale blobs.
|
// Cleanup any stale blobs.
|
||||||
for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
|
mBlobsMap.entrySet().removeIf(entry -> {
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
|
final BlobMetadata blobMetadata = entry.getValue();
|
||||||
userBlobs.entrySet().removeIf(entry -> {
|
|
||||||
final BlobMetadata blobMetadata = entry.getValue();
|
|
||||||
|
|
||||||
// Remove expired leases
|
// Remove expired leases
|
||||||
blobMetadata.removeExpiredLeases();
|
blobMetadata.removeExpiredLeases();
|
||||||
|
|
||||||
if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
|
if (blobMetadata.shouldBeDeleted(true /* respectLeaseWaitTime */)) {
|
||||||
deleteBlobLocked(blobMetadata);
|
deleteBlobLocked(blobMetadata);
|
||||||
deletedBlobIds.add(blobMetadata.getBlobId());
|
deletedBlobIds.add(blobMetadata.getBlobId());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
writeBlobsInfoAsync();
|
writeBlobsInfoAsync();
|
||||||
|
|
||||||
// Cleanup any stale sessions.
|
// Cleanup any stale sessions.
|
||||||
@ -1195,34 +1181,34 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
|
|
||||||
void runClearAllBlobs(@UserIdInt int userId) {
|
void runClearAllBlobs(@UserIdInt int userId) {
|
||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsLock) {
|
||||||
for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
|
mBlobsMap.entrySet().removeIf(entry -> {
|
||||||
final int blobUserId = mBlobsMap.keyAt(i);
|
final BlobMetadata blobMetadata = entry.getValue();
|
||||||
if (userId != UserHandle.USER_ALL && userId != blobUserId) {
|
if (userId == UserHandle.USER_ALL) {
|
||||||
continue;
|
mActiveBlobIds.remove(blobMetadata.getBlobId());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
|
blobMetadata.removeDataForUser(userId);
|
||||||
for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
|
if (blobMetadata.shouldBeDeleted(false /* respectLeaseWaitTime */)) {
|
||||||
mActiveBlobIds.remove(userBlobs.valueAt(j).getBlobId());
|
mActiveBlobIds.remove(blobMetadata.getBlobId());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
if (userId == UserHandle.USER_ALL) {
|
});
|
||||||
mBlobsMap.clear();
|
|
||||||
} else {
|
|
||||||
mBlobsMap.remove(userId);
|
|
||||||
}
|
|
||||||
writeBlobsInfoAsync();
|
writeBlobsInfoAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void deleteBlob(@NonNull BlobHandle blobHandle, @UserIdInt int userId) {
|
void deleteBlob(@NonNull BlobHandle blobHandle, @UserIdInt int userId) {
|
||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsLock) {
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
|
final BlobMetadata blobMetadata = mBlobsMap.get(blobHandle);
|
||||||
final BlobMetadata blobMetadata = userBlobs.get(blobHandle);
|
|
||||||
if (blobMetadata == null) {
|
if (blobMetadata == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
deleteBlobLocked(blobMetadata);
|
blobMetadata.removeDataForUser(userId);
|
||||||
userBlobs.remove(blobHandle);
|
if (blobMetadata.shouldBeDeleted(false /* respectLeaseWaitTime */)) {
|
||||||
|
deleteBlobLocked(blobMetadata);
|
||||||
|
mBlobsMap.remove(blobHandle);
|
||||||
|
}
|
||||||
writeBlobsInfoAsync();
|
writeBlobsInfoAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1235,11 +1221,12 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
|
|
||||||
boolean isBlobAvailable(long blobId, int userId) {
|
boolean isBlobAvailable(long blobId, int userId) {
|
||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsLock) {
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
|
for (int i = 0, blobCount = mBlobsMap.size(); i < blobCount; ++i) {
|
||||||
for (BlobMetadata blobMetadata : userBlobs.values()) {
|
final BlobMetadata blobMetadata = mBlobsMap.valueAt(i);
|
||||||
if (blobMetadata.getBlobId() == blobId) {
|
if (blobMetadata.getBlobId() != blobId) {
|
||||||
return true;
|
continue;
|
||||||
}
|
}
|
||||||
|
return blobMetadata.hasACommitterInUser(userId);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1274,27 +1261,22 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
|
|
||||||
@GuardedBy("mBlobsLock")
|
@GuardedBy("mBlobsLock")
|
||||||
private void dumpBlobsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
|
private void dumpBlobsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
|
||||||
for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
|
fout.println("List of blobs (" + mBlobsMap.size() + "):");
|
||||||
final int userId = mBlobsMap.keyAt(i);
|
fout.increaseIndent();
|
||||||
if (!dumpArgs.shouldDumpUser(userId)) {
|
for (int i = 0, blobCount = mBlobsMap.size(); i < blobCount; ++i) {
|
||||||
|
final BlobMetadata blobMetadata = mBlobsMap.valueAt(i);
|
||||||
|
if (!dumpArgs.shouldDumpBlob(blobMetadata.getBlobId())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
|
fout.println("Blob #" + blobMetadata.getBlobId());
|
||||||
fout.println("List of blobs in user #"
|
|
||||||
+ userId + " (" + userBlobs.size() + "):");
|
|
||||||
fout.increaseIndent();
|
fout.increaseIndent();
|
||||||
for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
|
blobMetadata.dump(fout, dumpArgs);
|
||||||
final BlobMetadata blobMetadata = userBlobs.valueAt(j);
|
|
||||||
if (!dumpArgs.shouldDumpBlob(blobMetadata.getBlobId())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
fout.println("Blob #" + blobMetadata.getBlobId());
|
|
||||||
fout.increaseIndent();
|
|
||||||
blobMetadata.dump(fout, dumpArgs);
|
|
||||||
fout.decreaseIndent();
|
|
||||||
}
|
|
||||||
fout.decreaseIndent();
|
fout.decreaseIndent();
|
||||||
}
|
}
|
||||||
|
if (mBlobsMap.isEmpty()) {
|
||||||
|
fout.println("<empty>");
|
||||||
|
}
|
||||||
|
fout.decreaseIndent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BlobStorageStatsAugmenter implements StorageStatsAugmenter {
|
private class BlobStorageStatsAugmenter implements StorageStatsAugmenter {
|
||||||
@ -1308,13 +1290,12 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
}, userId);
|
}, userId);
|
||||||
|
|
||||||
forEachBlobInUser(blobMetadata -> {
|
forEachBlob(blobMetadata -> {
|
||||||
if (blobMetadata.isALeasee(packageName)) {
|
if (blobMetadata.shouldAttributeToLeasee(packageName, userId,
|
||||||
if (!blobMetadata.hasOtherLeasees(packageName) || !callerHasStatsPermission) {
|
callerHasStatsPermission)) {
|
||||||
blobsDataSize.getAndAdd(blobMetadata.getSize());
|
blobsDataSize.getAndAdd(blobMetadata.getSize());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, userId);
|
});
|
||||||
|
|
||||||
stats.dataSize += blobsDataSize.get();
|
stats.dataSize += blobsDataSize.get();
|
||||||
}
|
}
|
||||||
@ -1330,13 +1311,12 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
}, userId);
|
}, userId);
|
||||||
|
|
||||||
forEachBlobInUser(blobMetadata -> {
|
forEachBlob(blobMetadata -> {
|
||||||
if (blobMetadata.isALeasee(uid)) {
|
if (blobMetadata.shouldAttributeToLeasee(uid,
|
||||||
if (!blobMetadata.hasOtherLeasees(uid) || !callerHasStatsPermission) {
|
callerHasStatsPermission)) {
|
||||||
blobsDataSize.getAndAdd(blobMetadata.getSize());
|
blobsDataSize.getAndAdd(blobMetadata.getSize());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, userId);
|
});
|
||||||
|
|
||||||
stats.dataSize += blobsDataSize.get();
|
stats.dataSize += blobsDataSize.get();
|
||||||
}
|
}
|
||||||
@ -1352,13 +1332,26 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void forEachBlobInUser(Consumer<BlobMetadata> consumer, int userId) {
|
private void forEachBlob(Consumer<BlobMetadata> consumer) {
|
||||||
synchronized (mBlobsLock) {
|
synchronized (mBlobsMap) {
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
|
forEachBlobLocked(consumer);
|
||||||
for (int i = 0, count = userBlobs.size(); i < count; ++i) {
|
}
|
||||||
final BlobMetadata blobMetadata = userBlobs.valueAt(i);
|
}
|
||||||
consumer.accept(blobMetadata);
|
|
||||||
}
|
@GuardedBy("mBlobsMap")
|
||||||
|
private void forEachBlobLocked(Consumer<BlobMetadata> consumer) {
|
||||||
|
for (int blobIdx = 0, count = mBlobsMap.size(); blobIdx < count; ++blobIdx) {
|
||||||
|
final BlobMetadata blobMetadata = mBlobsMap.valueAt(blobIdx);
|
||||||
|
consumer.accept(blobMetadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("mBlobsMap")
|
||||||
|
private void forEachBlobLocked(BiConsumer<BlobHandle, BlobMetadata> consumer) {
|
||||||
|
for (int blobIdx = 0, count = mBlobsMap.size(); blobIdx < count; ++blobIdx) {
|
||||||
|
final BlobHandle blobHandle = mBlobsMap.keyAt(blobIdx);
|
||||||
|
final BlobMetadata blobMetadata = mBlobsMap.valueAt(blobIdx);
|
||||||
|
consumer.accept(blobHandle, blobMetadata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1886,15 +1879,7 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int pullBlobData(int atomTag, List<StatsEvent> data) {
|
private int pullBlobData(int atomTag, List<StatsEvent> data) {
|
||||||
synchronized (mBlobsLock) {
|
forEachBlob(blobMetadata -> data.add(blobMetadata.dumpAsStatsEvent(atomTag)));
|
||||||
for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
|
|
||||||
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
|
|
||||||
for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
|
|
||||||
final BlobMetadata blob = userBlobs.valueAt(j);
|
|
||||||
data.add(blob.dumpAsStatsEvent(atomTag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return StatsManager.PULL_SUCCESS;
|
return StatsManager.PULL_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,12 +56,12 @@ import android.os.storage.StorageManager;
|
|||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import android.util.ExceptionUtils;
|
import android.util.ExceptionUtils;
|
||||||
|
import android.util.IndentingPrintWriter;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.FrameworkStatsLog;
|
import com.android.internal.util.FrameworkStatsLog;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
|
||||||
import com.android.internal.util.Preconditions;
|
import com.android.internal.util.Preconditions;
|
||||||
import com.android.internal.util.XmlUtils;
|
import com.android.internal.util.XmlUtils;
|
||||||
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
|
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
|
||||||
|
@ -35,7 +35,6 @@ import android.os.Looper;
|
|||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.platform.test.annotations.Presubmit;
|
import android.platform.test.annotations.Presubmit;
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.LongSparseArray;
|
import android.util.LongSparseArray;
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
import androidx.test.InstrumentationRegistry;
|
||||||
@ -68,7 +67,6 @@ public class BlobStoreManagerServiceTest {
|
|||||||
private File mBlobsDir;
|
private File mBlobsDir;
|
||||||
|
|
||||||
private LongSparseArray<BlobStoreSession> mUserSessions;
|
private LongSparseArray<BlobStoreSession> mUserSessions;
|
||||||
private ArrayMap<BlobHandle, BlobMetadata> mUserBlobs;
|
|
||||||
|
|
||||||
private static final String TEST_PKG1 = "com.example1";
|
private static final String TEST_PKG1 = "com.example1";
|
||||||
private static final String TEST_PKG2 = "com.example2";
|
private static final String TEST_PKG2 = "com.example2";
|
||||||
@ -99,10 +97,8 @@ public class BlobStoreManagerServiceTest {
|
|||||||
mHandler = new TestHandler(Looper.getMainLooper());
|
mHandler = new TestHandler(Looper.getMainLooper());
|
||||||
mService = new BlobStoreManagerService(mContext, new TestInjector());
|
mService = new BlobStoreManagerService(mContext, new TestInjector());
|
||||||
mUserSessions = new LongSparseArray<>();
|
mUserSessions = new LongSparseArray<>();
|
||||||
mUserBlobs = new ArrayMap<>();
|
|
||||||
|
|
||||||
mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId());
|
mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId());
|
||||||
mService.addUserBlobsForTest(mUserBlobs, UserHandle.myUserId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -147,7 +143,7 @@ public class BlobStoreManagerServiceTest {
|
|||||||
final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1,
|
final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1,
|
||||||
blobHandle1, true /* hasLeases */);
|
blobHandle1, true /* hasLeases */);
|
||||||
doReturn(true).when(blobMetadata1).isACommitter(TEST_PKG1, TEST_UID1);
|
doReturn(true).when(blobMetadata1).isACommitter(TEST_PKG1, TEST_UID1);
|
||||||
mUserBlobs.put(blobHandle1, blobMetadata1);
|
addBlob(blobHandle1, blobMetadata1);
|
||||||
|
|
||||||
final long blobId2 = 347;
|
final long blobId2 = 347;
|
||||||
final File blobFile2 = mock(File.class);
|
final File blobFile2 = mock(File.class);
|
||||||
@ -156,7 +152,7 @@ public class BlobStoreManagerServiceTest {
|
|||||||
final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2,
|
final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2,
|
||||||
blobHandle2, false /* hasLeases */);
|
blobHandle2, false /* hasLeases */);
|
||||||
doReturn(false).when(blobMetadata2).isACommitter(TEST_PKG1, TEST_UID1);
|
doReturn(false).when(blobMetadata2).isACommitter(TEST_PKG1, TEST_UID1);
|
||||||
mUserBlobs.put(blobHandle2, blobMetadata2);
|
addBlob(blobHandle2, blobMetadata2);
|
||||||
|
|
||||||
final long blobId3 = 49875;
|
final long blobId3 = 49875;
|
||||||
final File blobFile3 = mock(File.class);
|
final File blobFile3 = mock(File.class);
|
||||||
@ -165,7 +161,7 @@ public class BlobStoreManagerServiceTest {
|
|||||||
final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3,
|
final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3,
|
||||||
blobHandle3, true /* hasLeases */);
|
blobHandle3, true /* hasLeases */);
|
||||||
doReturn(true).when(blobMetadata3).isACommitter(TEST_PKG1, TEST_UID1);
|
doReturn(true).when(blobMetadata3).isACommitter(TEST_PKG1, TEST_UID1);
|
||||||
mUserBlobs.put(blobHandle3, blobMetadata3);
|
addBlob(blobHandle3, blobMetadata3);
|
||||||
|
|
||||||
mService.addActiveIdsForTest(sessionId1, sessionId2, sessionId3, sessionId4,
|
mService.addActiveIdsForTest(sessionId1, sessionId2, sessionId3, sessionId4,
|
||||||
blobId1, blobId2, blobId3);
|
blobId1, blobId2, blobId3);
|
||||||
@ -197,10 +193,10 @@ public class BlobStoreManagerServiceTest {
|
|||||||
verify(blobMetadata2).destroy();
|
verify(blobMetadata2).destroy();
|
||||||
verify(blobMetadata3).destroy();
|
verify(blobMetadata3).destroy();
|
||||||
|
|
||||||
assertThat(mUserBlobs.size()).isEqualTo(1);
|
assertThat(mService.getBlobsCountForTest()).isEqualTo(1);
|
||||||
assertThat(mUserBlobs.get(blobHandle1)).isNotNull();
|
assertThat(mService.getBlobForTest(blobHandle1)).isNotNull();
|
||||||
assertThat(mUserBlobs.get(blobHandle2)).isNull();
|
assertThat(mService.getBlobForTest(blobHandle2)).isNull();
|
||||||
assertThat(mUserBlobs.get(blobHandle3)).isNull();
|
assertThat(mService.getBlobForTest(blobHandle3)).isNull();
|
||||||
|
|
||||||
assertThat(mService.getActiveIdsForTest()).containsExactly(
|
assertThat(mService.getActiveIdsForTest()).containsExactly(
|
||||||
sessionId2, sessionId3, blobId1);
|
sessionId2, sessionId3, blobId1);
|
||||||
@ -293,7 +289,7 @@ public class BlobStoreManagerServiceTest {
|
|||||||
"label1", System.currentTimeMillis() - 2000, "tag1");
|
"label1", System.currentTimeMillis() - 2000, "tag1");
|
||||||
final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, blobHandle1,
|
final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, blobHandle1,
|
||||||
true /* hasLeases */);
|
true /* hasLeases */);
|
||||||
mUserBlobs.put(blobHandle1, blobMetadata1);
|
addBlob(blobHandle1, blobMetadata1);
|
||||||
|
|
||||||
final long blobId2 = 78974;
|
final long blobId2 = 78974;
|
||||||
final File blobFile2 = mock(File.class);
|
final File blobFile2 = mock(File.class);
|
||||||
@ -301,7 +297,7 @@ public class BlobStoreManagerServiceTest {
|
|||||||
"label2", System.currentTimeMillis() + 30000, "tag2");
|
"label2", System.currentTimeMillis() + 30000, "tag2");
|
||||||
final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, blobHandle2,
|
final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, blobHandle2,
|
||||||
true /* hasLeases */);
|
true /* hasLeases */);
|
||||||
mUserBlobs.put(blobHandle2, blobMetadata2);
|
addBlob(blobHandle2, blobMetadata2);
|
||||||
|
|
||||||
final long blobId3 = 97;
|
final long blobId3 = 97;
|
||||||
final File blobFile3 = mock(File.class);
|
final File blobFile3 = mock(File.class);
|
||||||
@ -309,7 +305,7 @@ public class BlobStoreManagerServiceTest {
|
|||||||
"label3", System.currentTimeMillis() + 4400000, "tag3");
|
"label3", System.currentTimeMillis() + 4400000, "tag3");
|
||||||
final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3, blobHandle3,
|
final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3, blobHandle3,
|
||||||
false /* hasLeases */);
|
false /* hasLeases */);
|
||||||
mUserBlobs.put(blobHandle3, blobMetadata3);
|
addBlob(blobHandle3, blobMetadata3);
|
||||||
|
|
||||||
mService.addActiveIdsForTest(blobId1, blobId2, blobId3);
|
mService.addActiveIdsForTest(blobId1, blobId2, blobId3);
|
||||||
|
|
||||||
@ -321,8 +317,8 @@ public class BlobStoreManagerServiceTest {
|
|||||||
verify(blobMetadata2, never()).destroy();
|
verify(blobMetadata2, never()).destroy();
|
||||||
verify(blobMetadata3).destroy();
|
verify(blobMetadata3).destroy();
|
||||||
|
|
||||||
assertThat(mUserBlobs.size()).isEqualTo(1);
|
assertThat(mService.getBlobsCountForTest()).isEqualTo(1);
|
||||||
assertThat(mUserBlobs.get(blobHandle2)).isNotNull();
|
assertThat(mService.getBlobForTest(blobHandle2)).isNotNull();
|
||||||
|
|
||||||
assertThat(mService.getActiveIdsForTest()).containsExactly(blobId2);
|
assertThat(mService.getActiveIdsForTest()).containsExactly(blobId2);
|
||||||
assertThat(mService.getKnownIdsForTest()).containsExactly(blobId1, blobId2, blobId3);
|
assertThat(mService.getKnownIdsForTest()).containsExactly(blobId1, blobId2, blobId3);
|
||||||
@ -336,21 +332,21 @@ public class BlobStoreManagerServiceTest {
|
|||||||
doReturn(size1).when(blobMetadata1).getSize();
|
doReturn(size1).when(blobMetadata1).getSize();
|
||||||
doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG1, TEST_UID1);
|
doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG1, TEST_UID1);
|
||||||
doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG2, TEST_UID2);
|
doReturn(true).when(blobMetadata1).isALeasee(TEST_PKG2, TEST_UID2);
|
||||||
mUserBlobs.put(mock(BlobHandle.class), blobMetadata1);
|
addBlob(mock(BlobHandle.class), blobMetadata1);
|
||||||
|
|
||||||
final BlobMetadata blobMetadata2 = mock(BlobMetadata.class);
|
final BlobMetadata blobMetadata2 = mock(BlobMetadata.class);
|
||||||
final long size2 = 89475;
|
final long size2 = 89475;
|
||||||
doReturn(size2).when(blobMetadata2).getSize();
|
doReturn(size2).when(blobMetadata2).getSize();
|
||||||
doReturn(false).when(blobMetadata2).isALeasee(TEST_PKG1, TEST_UID1);
|
doReturn(false).when(blobMetadata2).isALeasee(TEST_PKG1, TEST_UID1);
|
||||||
doReturn(true).when(blobMetadata2).isALeasee(TEST_PKG2, TEST_UID2);
|
doReturn(true).when(blobMetadata2).isALeasee(TEST_PKG2, TEST_UID2);
|
||||||
mUserBlobs.put(mock(BlobHandle.class), blobMetadata2);
|
addBlob(mock(BlobHandle.class), blobMetadata2);
|
||||||
|
|
||||||
final BlobMetadata blobMetadata3 = mock(BlobMetadata.class);
|
final BlobMetadata blobMetadata3 = mock(BlobMetadata.class);
|
||||||
final long size3 = 328732;
|
final long size3 = 328732;
|
||||||
doReturn(size3).when(blobMetadata3).getSize();
|
doReturn(size3).when(blobMetadata3).getSize();
|
||||||
doReturn(true).when(blobMetadata3).isALeasee(TEST_PKG1, TEST_UID1);
|
doReturn(true).when(blobMetadata3).isALeasee(TEST_PKG1, TEST_UID1);
|
||||||
doReturn(false).when(blobMetadata3).isALeasee(TEST_PKG2, TEST_UID2);
|
doReturn(false).when(blobMetadata3).isALeasee(TEST_PKG2, TEST_UID2);
|
||||||
mUserBlobs.put(mock(BlobHandle.class), blobMetadata3);
|
addBlob(mock(BlobHandle.class), blobMetadata3);
|
||||||
|
|
||||||
// Verify usage is calculated correctly
|
// Verify usage is calculated correctly
|
||||||
assertThat(mService.getTotalUsageBytesLocked(TEST_UID1, TEST_PKG1))
|
assertThat(mService.getTotalUsageBytesLocked(TEST_UID1, TEST_PKG1))
|
||||||
@ -388,6 +384,11 @@ public class BlobStoreManagerServiceTest {
|
|||||||
return blobMetadata;
|
return blobMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addBlob(BlobHandle blobHandle, BlobMetadata blobMetadata) {
|
||||||
|
doReturn(blobHandle).when(blobMetadata).getBlobHandle();
|
||||||
|
mService.addBlobLocked(blobMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
private class TestHandler extends Handler {
|
private class TestHandler extends Handler {
|
||||||
TestHandler(Looper looper) {
|
TestHandler(Looper looper) {
|
||||||
super(looper);
|
super(looper);
|
||||||
|
@ -23,6 +23,7 @@ import android.app.blob.BlobStoreManager;
|
|||||||
import android.app.blob.LeaseInfo;
|
import android.app.blob.LeaseInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.os.FileUtils;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@ -56,6 +57,16 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input)
|
||||||
|
throws IOException {
|
||||||
|
try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) {
|
||||||
|
try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(
|
||||||
|
session.openWrite(0, -1))) {
|
||||||
|
FileUtils.copy(in, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input,
|
public static void writeToSession(BlobStoreManager.Session session, ParcelFileDescriptor input,
|
||||||
long lengthBytes) throws IOException {
|
long lengthBytes) throws IOException {
|
||||||
try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) {
|
try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(input)) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user