Add a variant of acquireLease() which takes a string description.

Bug: 148797775
Test: atest cts/tests/BlobStore/src/com/android/cts/blob/BlobStoreManagerTest.java
Change-Id: I28df1e8a144d61732cdcbbcb6e6b096f22671bcf
This commit is contained in:
Sudheer Shanka
2020-02-03 12:27:24 -08:00
parent a17b58c06a
commit 1406bc8422
8 changed files with 172 additions and 36 deletions

View File

@@ -142,6 +142,9 @@ public class BlobStoreManager {
/** @hide */
public static final int COMMIT_RESULT_ERROR = 1;
/** @hide */
public static final int INVALID_RES_ID = -1;
private final Context mContext;
private final IBlobStoreManager mService;
@@ -285,11 +288,65 @@ public class BlobStoreManager {
* caller is trying to acquire too many leases.
*
* @see {@link #acquireLease(BlobHandle, int)}
* @see {@link #acquireLease(BlobHandle, CharSequence)}
*/
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
@CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
try {
mService.acquireLease(blobHandle, descriptionResId, leaseExpiryTimeMillis,
mService.acquireLease(blobHandle, descriptionResId, null, leaseExpiryTimeMillis,
mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
* system that the caller wants the blob to be kept around.
*
* <p> This is variant of {@link #acquireLease(BlobHandle, int, long)} taking a
* {@link CharSequence} for {@code description}. It is highly recommended that callers only
* use this when a valid resource ID for {@code description} could not be provided. Otherwise,
* apps should prefer using {@link #acquireLease(BlobHandle, int)} which will allow
* {@code description} to be localized.
*
* <p> Any active leases will be automatically released when the blob's expiry time
* ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed.
*
* <p> This lease information is persisted and calling this more than once will result in
* latest lease overriding any previous lease.
*
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
* acquire a lease for.
* @param description a short description string that can be surfaced
* to the user explaining what the blob is used for.
* @param leaseExpiryTimeMillis the time in milliseconds after which the lease can be
* automatically released, in {@link System#currentTimeMillis()}
* timebase. If its value is {@code 0}, then the behavior of this
* API is identical to {@link #acquireLease(BlobHandle, int)}
* where clients have to explicitly call
* {@link #releaseLease(BlobHandle)} when they don't
* need the blob anymore.
*
* @throws IOException when there is an I/O error while acquiring a lease to the blob.
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
* exist or the caller does not have access to it.
* @throws IllegalArgumentException when {@code blobHandle} is invalid or
* if the {@code leaseExpiryTimeMillis} is greater than the
* {@link BlobHandle#getExpiryTimeMillis()}.
* @throws IllegalStateException when a lease could not be acquired, such as when the
* caller is trying to acquire too many leases.
*
* @see {@link #acquireLease(BlobHandle, int, long)}
* @see {@link #acquireLease(BlobHandle, CharSequence)}
*/
public void acquireLease(@NonNull BlobHandle blobHandle, @NonNull CharSequence description,
@CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
try {
mService.acquireLease(blobHandle, INVALID_RES_ID, description, leaseExpiryTimeMillis,
mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
@@ -327,12 +384,54 @@ public class BlobStoreManager {
* caller is trying to acquire too many leases.
*
* @see {@link #acquireLease(BlobHandle, int, long)}
* @see {@link #acquireLease(BlobHandle, CharSequence, long)}
*/
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId)
throws IOException {
acquireLease(blobHandle, descriptionResId, 0);
}
/**
* Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
* system that the caller wants the blob to be kept around.
*
* <p> This is variant of {@link #acquireLease(BlobHandle, int)} taking a {@link CharSequence}
* for {@code description}. It is highly recommended that callers only use this when a valid
* resource ID for {@code description} could not be provided. Otherwise, apps should prefer
* using {@link #acquireLease(BlobHandle, int)} which will allow {@code description} to be
* localized.
*
* <p> This is similar to {@link #acquireLease(BlobHandle, CharSequence, long)} except clients
* don't have to specify the lease expiry time upfront using this API and need to explicitly
* release the lease using {@link #releaseLease(BlobHandle)} when they no longer like to keep
* a blob around.
*
* <p> Any active leases will be automatically released when the blob's expiry time
* ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed.
*
* <p> This lease information is persisted and calling this more than once will result in
* latest lease overriding any previous lease.
*
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
* acquire a lease for.
* @param description a short description string that can be surfaced
* to the user explaining what the blob is used for.
*
* @throws IOException when there is an I/O error while acquiring a lease to the blob.
* @throws SecurityException when the blob represented by the {@code blobHandle} does not
* exist or the caller does not have access to it.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
* @throws IllegalStateException when a lease could not be acquired, such as when the
* caller is trying to acquire too many leases.
*
* @see {@link #acquireLease(BlobHandle, int)}
* @see {@link #acquireLease(BlobHandle, CharSequence, long)}
*/
public void acquireLease(@NonNull BlobHandle blobHandle, @NonNull CharSequence description)
throws IOException {
acquireLease(blobHandle, description, 0);
}
/**
* Release all active leases to the blob represented by {@code blobHandle} which are
* currently held by the caller.

View File

@@ -26,8 +26,8 @@ interface IBlobStoreManager {
ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName);
void deleteSession(long sessionId, in String packageName);
void acquireLease(in BlobHandle handle, int descriptionResId, long leaseTimeout,
in String packageName);
void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description,
long leaseTimeoutMillis, in String packageName);
void releaseLease(in BlobHandle handle, in String packageName);
void waitForIdle(in RemoteCallback callback);

View File

@@ -52,4 +52,5 @@ public final class XmlTags {
// For leasee
public static final String TAG_LEASEE = "l";
public static final String ATTR_DESCRIPTION_RES_ID = "rid";
public static final String ATTR_DESCRIPTION = "d";
}

View File

@@ -15,6 +15,7 @@
*/
package com.android.server.blob;
import static android.app.blob.XmlTags.ATTR_DESCRIPTION;
import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_ID;
import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
import static android.app.blob.XmlTags.ATTR_ID;
@@ -28,12 +29,14 @@ import static android.app.blob.XmlTags.TAG_LEASEE;
import static android.system.OsConstants.O_RDONLY;
import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.blob.BlobHandle;
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;
@@ -141,11 +144,11 @@ class BlobMetadata {
}
}
void addLeasee(String callingPackage, int callingUid,
int descriptionResId, long leaseExpiryTimeMillis) {
void addLeasee(String callingPackage, int callingUid, int descriptionResId,
CharSequence description, long leaseExpiryTimeMillis) {
synchronized (mMetadataLock) {
mLeasees.add(new Leasee(callingPackage, callingUid,
descriptionResId, leaseExpiryTimeMillis));
descriptionResId, description, leaseExpiryTimeMillis));
}
}
@@ -308,7 +311,7 @@ class BlobMetadata {
}
@Nullable
static BlobMetadata createFromXml(Context context, XmlPullParser in)
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);
@@ -321,12 +324,12 @@ class BlobMetadata {
if (TAG_BLOB_HANDLE.equals(in.getName())) {
blobHandle = BlobHandle.createFromXml(in);
} else if (TAG_COMMITTER.equals(in.getName())) {
final Committer committer = Committer.createFromXml(in);
final Committer committer = Committer.createFromXml(in, version);
if (committer != null) {
committers.add(committer);
}
} else if (TAG_LEASEE.equals(in.getName())) {
leasees.add(Leasee.createFromXml(in));
leasees.add(Leasee.createFromXml(in, version));
}
}
@@ -366,7 +369,7 @@ class BlobMetadata {
}
@Nullable
static Committer createFromXml(@NonNull XmlPullParser in)
static Committer createFromXml(@NonNull XmlPullParser in, int version)
throws XmlPullParserException, IOException {
final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
@@ -388,12 +391,15 @@ class BlobMetadata {
static final class Leasee extends Accessor {
public final int descriptionResId;
public final CharSequence description;
public final long expiryTimeMillis;
Leasee(String packageName, int uid, int descriptionResId, long expiryTimeMillis) {
Leasee(String packageName, int uid, int descriptionResId, CharSequence description,
long expiryTimeMillis) {
super(packageName, uid);
this.descriptionResId = descriptionResId;
this.expiryTimeMillis = expiryTimeMillis;
this.description = description;
}
boolean isStillValid() {
@@ -401,35 +407,51 @@ class BlobMetadata {
}
void dump(Context context, IndentingPrintWriter fout) {
String desc = null;
try {
final Resources leaseeRes = context.getPackageManager()
.getResourcesForApplicationAsUser(packageName, UserHandle.getUserId(uid));
desc = leaseeRes.getString(descriptionResId);
} catch (PackageManager.NameNotFoundException e) {
Slog.d(TAG, "Unknown package in user " + UserHandle.getUserId(uid) + ": "
+ packageName, e);
desc = "<none>";
}
fout.println("desc: " + desc);
fout.println("desc: " + getDescriptionToDump(context));
fout.println("expiryMs: " + expiryTimeMillis);
}
private String getDescriptionToDump(Context context) {
String desc = null;
if (ResourceId.isValid(descriptionResId)) {
try {
final Resources leaseeRes = context.getPackageManager()
.getResourcesForApplicationAsUser(
packageName, UserHandle.getUserId(uid));
desc = leaseeRes.getString(descriptionResId);
} catch (PackageManager.NameNotFoundException e) {
Slog.d(TAG, "Unknown package in user " + UserHandle.getUserId(uid) + ": "
+ packageName, e);
desc = "<none>";
}
} else {
desc = description.toString();
}
return desc;
}
void writeToXml(@NonNull XmlSerializer out) throws IOException {
XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
XmlUtils.writeIntAttribute(out, ATTR_DESCRIPTION_RES_ID, descriptionResId);
XmlUtils.writeLongAttribute(out, ATTR_EXPIRY_TIME, expiryTimeMillis);
XmlUtils.writeStringAttribute(out, ATTR_DESCRIPTION, description);
}
@NonNull
static Leasee createFromXml(@NonNull XmlPullParser in) throws IOException {
static Leasee createFromXml(@NonNull XmlPullParser in, int version) throws IOException {
final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
final int descriptionResId = XmlUtils.readIntAttribute(in, ATTR_DESCRIPTION_RES_ID);
final long expiryTimeMillis = XmlUtils.readLongAttribute(in, ATTR_EXPIRY_TIME);
final CharSequence description;
if (version >= XML_VERSION_ADD_STRING_DESC) {
description = XmlUtils.readStringAttribute(in, ATTR_DESCRIPTION);
} else {
description = null;
}
return new Leasee(packageName, uid, descriptionResId, expiryTimeMillis);
return new Leasee(packageName, uid, descriptionResId, description, expiryTimeMillis);
}
}

View File

@@ -28,7 +28,12 @@ class BlobStoreConfig {
public static final String TAG = "BlobStore";
public static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
public static final int CURRENT_XML_VERSION = 1;
// Initial version.
public static final int XML_VERSION_INIT = 1;
// Added a string variant of lease description.
public static final int XML_VERSION_ADD_STRING_DESC = 2;
public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_STRING_DESC;
private static final String ROOT_DIR_NAME = "blobstore";
private static final String BLOBS_DIR_NAME = "blobs";

View File

@@ -27,10 +27,10 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.os.UserHandle.USER_NULL;
import static com.android.server.blob.BlobStoreConfig.CURRENT_XML_VERSION;
import static com.android.server.blob.BlobStoreConfig.LOGV;
import static com.android.server.blob.BlobStoreConfig.SESSION_EXPIRY_TIMEOUT_MILLIS;
import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED;
import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
@@ -324,7 +324,8 @@ public class BlobStoreManagerService extends SystemService {
}
private void acquireLeaseInternal(BlobHandle blobHandle, int descriptionResId,
long leaseExpiryTimeMillis, int callingUid, String callingPackage) {
CharSequence description, long leaseExpiryTimeMillis,
int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
.get(blobHandle);
@@ -338,7 +339,7 @@ public class BlobStoreManagerService extends SystemService {
"Lease expiry cannot be later than blobs expiry time");
}
blobMetadata.addLeasee(callingPackage, callingUid,
descriptionResId, leaseExpiryTimeMillis);
descriptionResId, description, leaseExpiryTimeMillis);
if (LOGV) {
Slog.v(TAG, "Acquired lease on " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
@@ -450,7 +451,7 @@ public class BlobStoreManagerService extends SystemService {
out.setOutput(fos, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
out.startTag(null, TAG_SESSIONS);
XmlUtils.writeIntAttribute(out, ATTR_VERSION, CURRENT_XML_VERSION);
XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
final LongSparseArray<BlobStoreSession> userSessions =
@@ -491,6 +492,7 @@ public class BlobStoreManagerService extends SystemService {
final XmlPullParser in = Xml.newPullParser();
in.setInput(fis, StandardCharsets.UTF_8.name());
XmlUtils.beginDocument(in, TAG_SESSIONS);
final int version = XmlUtils.readIntAttribute(in, ATTR_VERSION);
while (true) {
XmlUtils.nextElement(in);
if (in.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -499,7 +501,7 @@ public class BlobStoreManagerService extends SystemService {
if (TAG_SESSION.equals(in.getName())) {
final BlobStoreSession session = BlobStoreSession.createFromXml(
in, mContext, mSessionStateChangeListener);
in, version, mContext, mSessionStateChangeListener);
if (session == null) {
continue;
}
@@ -539,7 +541,7 @@ public class BlobStoreManagerService extends SystemService {
out.setOutput(fos, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
out.startTag(null, TAG_BLOBS);
XmlUtils.writeIntAttribute(out, ATTR_VERSION, CURRENT_XML_VERSION);
XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
@@ -579,6 +581,7 @@ public class BlobStoreManagerService extends SystemService {
final XmlPullParser in = Xml.newPullParser();
in.setInput(fis, StandardCharsets.UTF_8.name());
XmlUtils.beginDocument(in, TAG_BLOBS);
final int version = XmlUtils.readIntAttribute(in, ATTR_VERSION);
while (true) {
XmlUtils.nextElement(in);
if (in.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -586,7 +589,8 @@ public class BlobStoreManagerService extends SystemService {
}
if (TAG_BLOB.equals(in.getName())) {
final BlobMetadata blobMetadata = BlobMetadata.createFromXml(mContext, in);
final BlobMetadata blobMetadata = BlobMetadata.createFromXml(
in, version, mContext);
final SparseArray<String> userPackages = allPackages.get(
blobMetadata.getUserId());
if (userPackages == null) {
@@ -1032,11 +1036,14 @@ public class BlobStoreManagerService extends SystemService {
@Override
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
@Nullable CharSequence description,
@CurrentTimeSecondsLong long leaseExpiryTimeMillis, @NonNull String packageName) {
Objects.requireNonNull(blobHandle, "blobHandle must not be null");
blobHandle.assertIsValid();
Preconditions.checkArgument(ResourceId.isValid(descriptionResId),
"descriptionResId is not valid");
Preconditions.checkArgument(
ResourceId.isValid(descriptionResId) || description != null,
"Description must be valid; descriptionId=" + descriptionResId
+ ", description=" + description);
Preconditions.checkArgumentNonnegative(leaseExpiryTimeMillis,
"leaseExpiryTimeMillis must not be negative");
Objects.requireNonNull(packageName, "packageName must not be null");
@@ -1044,7 +1051,7 @@ public class BlobStoreManagerService extends SystemService {
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
acquireLeaseInternal(blobHandle, descriptionResId, leaseExpiryTimeMillis,
acquireLeaseInternal(blobHandle, descriptionResId, description, leaseExpiryTimeMillis,
callingUid, packageName);
}

View File

@@ -511,7 +511,7 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
}
@Nullable
static BlobStoreSession createFromXml(@NonNull XmlPullParser in,
static BlobStoreSession createFromXml(@NonNull XmlPullParser in, int version,
@NonNull Context context, @NonNull SessionStateChangeListener stateChangeListener)
throws IOException, XmlPullParserException {
final int sessionId = XmlUtils.readIntAttribute(in, ATTR_ID);

View File

@@ -7560,7 +7560,9 @@ package android.app.blob {
public class BlobStoreManager {
method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int, long) throws java.io.IOException;
method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence, long) throws java.io.IOException;
method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int) throws java.io.IOException;
method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence) throws java.io.IOException;
method @IntRange(from=1) public long createSession(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
method public void deleteSession(@IntRange(from=1) long) throws java.io.IOException;
method @NonNull public android.os.ParcelFileDescriptor openBlob(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;