Don't persist description res ids as they are subject to change.
Instead persist the resource name and update the javadoc to suggest that apps should re-acquire the lease if the description resource changes. Bug: 150829640 Test: atest --test-mapping apex/blobstore Change-Id: Ibeabffe7de046ed74cbbcaed6c69c9d25028799d
This commit is contained in:
@@ -145,6 +145,9 @@ public class BlobStoreManager {
|
||||
/** @hide */
|
||||
public static final int INVALID_RES_ID = -1;
|
||||
|
||||
/** @hide */
|
||||
public static final String DESC_RES_TYPE_STRING = "string";
|
||||
|
||||
private final Context mContext;
|
||||
private final IBlobStoreManager mService;
|
||||
|
||||
@@ -269,6 +272,9 @@ public class BlobStoreManager {
|
||||
* <p> When an app acquires a lease on a blob, the System will try to keep this
|
||||
* blob around but note that it can still be deleted if it was requested by the user.
|
||||
*
|
||||
* <p> In case the resource name for the {@code descriptionResId} is modified as part of
|
||||
* an app update, apps should re-acquire the lease with the new resource id.
|
||||
*
|
||||
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
|
||||
* acquire a lease for.
|
||||
* @param descriptionResId the resource id for a short description string that can be surfaced
|
||||
@@ -380,6 +386,9 @@ public class BlobStoreManager {
|
||||
* <p> When an app acquires a lease on a blob, the System will try to keep this
|
||||
* blob around but note that it can still be deleted if it was requested by the user.
|
||||
*
|
||||
* <p> In case the resource name for the {@code descriptionResId} is modified as part of
|
||||
* an app update, apps should re-acquire the lease with the new resource id.
|
||||
*
|
||||
* @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
|
||||
* acquire a lease for.
|
||||
* @param descriptionResId the resource id for a short description string that can be surfaced
|
||||
|
||||
@@ -51,6 +51,6 @@ 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_RES_NAME = "rn";
|
||||
public static final String ATTR_DESCRIPTION = "d";
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
package com.android.server.blob;
|
||||
|
||||
import static android.app.blob.BlobStoreManager.DESC_RES_TYPE_STRING;
|
||||
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_DESCRIPTION_RES_NAME;
|
||||
import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
|
||||
import static android.app.blob.XmlTags.ATTR_ID;
|
||||
import static android.app.blob.XmlTags.ATTR_PACKAGE;
|
||||
@@ -29,7 +30,9 @@ import static android.app.blob.XmlTags.TAG_LEASEE;
|
||||
import static android.os.Process.INVALID_UID;
|
||||
import static android.system.OsConstants.O_RDONLY;
|
||||
|
||||
import static com.android.server.blob.BlobStoreConfig.LOGV;
|
||||
import static com.android.server.blob.BlobStoreConfig.TAG;
|
||||
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 android.annotation.NonNull;
|
||||
@@ -154,7 +157,7 @@ class BlobMetadata {
|
||||
synchronized (mMetadataLock) {
|
||||
// We need to override the leasee data, so first remove any existing
|
||||
// leasee before adding the new one.
|
||||
final Leasee leasee = new Leasee(callingPackage, callingUid,
|
||||
final Leasee leasee = new Leasee(mContext, callingPackage, callingUid,
|
||||
descriptionResId, description, leaseExpiryTimeMillis);
|
||||
mLeasees.remove(leasee);
|
||||
mLeasees.add(leasee);
|
||||
@@ -459,59 +462,123 @@ class BlobMetadata {
|
||||
}
|
||||
|
||||
static final class Leasee extends Accessor {
|
||||
public final int descriptionResId;
|
||||
public final String descriptionResEntryName;
|
||||
public final CharSequence description;
|
||||
public final long expiryTimeMillis;
|
||||
|
||||
Leasee(String packageName, int uid, int descriptionResId, CharSequence description,
|
||||
long expiryTimeMillis) {
|
||||
Leasee(@NonNull Context context, @NonNull String packageName,
|
||||
int uid, int descriptionResId,
|
||||
@Nullable CharSequence description, long expiryTimeMillis) {
|
||||
super(packageName, uid);
|
||||
this.descriptionResId = descriptionResId;
|
||||
final Resources packageResources = getPackageResources(context, packageName,
|
||||
UserHandle.getUserId(uid));
|
||||
this.descriptionResEntryName = getResourceEntryName(packageResources, descriptionResId);
|
||||
this.expiryTimeMillis = expiryTimeMillis;
|
||||
this.description = description == null
|
||||
? getDescription(packageResources, descriptionResId)
|
||||
: description;
|
||||
}
|
||||
|
||||
Leasee(String packageName, int uid, @Nullable String descriptionResEntryName,
|
||||
@Nullable CharSequence description, long expiryTimeMillis) {
|
||||
super(packageName, uid);
|
||||
this.descriptionResEntryName = descriptionResEntryName;
|
||||
this.expiryTimeMillis = expiryTimeMillis;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getResourceEntryName(@Nullable Resources packageResources,
|
||||
int resId) {
|
||||
if (!ResourceId.isValid(resId) || packageResources == null) {
|
||||
return null;
|
||||
}
|
||||
return packageResources.getResourceEntryName(resId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getDescription(@NonNull Context context,
|
||||
@NonNull String descriptionResEntryName, @NonNull String packageName, int userId) {
|
||||
if (descriptionResEntryName == null || descriptionResEntryName.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final Resources resources = getPackageResources(context, packageName, userId);
|
||||
if (resources == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final int resId = resources.getIdentifier(descriptionResEntryName,
|
||||
DESC_RES_TYPE_STRING, packageName);
|
||||
return resId <= 0 ? null : resources.getString(resId);
|
||||
} catch (Resources.NotFoundException e) {
|
||||
if (LOGV) {
|
||||
Slog.w(TAG, "Description resource not found", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getDescription(@Nullable Resources packageResources,
|
||||
int descriptionResId) {
|
||||
if (!ResourceId.isValid(descriptionResId) || packageResources == null) {
|
||||
return null;
|
||||
}
|
||||
return packageResources.getString(descriptionResId);
|
||||
}
|
||||
|
||||
boolean isStillValid() {
|
||||
return expiryTimeMillis == 0 || expiryTimeMillis <= System.currentTimeMillis();
|
||||
}
|
||||
|
||||
void dump(Context context, IndentingPrintWriter fout) {
|
||||
void dump(@NonNull Context context, @NonNull IndentingPrintWriter fout) {
|
||||
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 {
|
||||
@NonNull
|
||||
private String getDescriptionToDump(@NonNull Context context) {
|
||||
String desc = getDescription(context, descriptionResEntryName, packageName,
|
||||
UserHandle.getUserId(uid));
|
||||
if (desc == null) {
|
||||
desc = description.toString();
|
||||
}
|
||||
return desc;
|
||||
return desc == null ? "<none>" : desc;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Resources getPackageResources(@NonNull Context context,
|
||||
@NonNull String packageName, int userId) {
|
||||
try {
|
||||
return context.getPackageManager()
|
||||
.getResourcesForApplicationAsUser(packageName, userId);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.d(TAG, "Unknown package in user " + userId + ": "
|
||||
+ packageName, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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.writeStringAttribute(out, ATTR_DESCRIPTION_RES_NAME, descriptionResEntryName);
|
||||
XmlUtils.writeLongAttribute(out, ATTR_EXPIRY_TIME, expiryTimeMillis);
|
||||
XmlUtils.writeStringAttribute(out, ATTR_DESCRIPTION, description);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static Leasee createFromXml(@NonNull XmlPullParser in, int version) 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 String descriptionResEntryName;
|
||||
if (version >= XML_VERSION_ADD_DESC_RES_NAME) {
|
||||
descriptionResEntryName = XmlUtils.readStringAttribute(
|
||||
in, ATTR_DESCRIPTION_RES_NAME);
|
||||
} else {
|
||||
descriptionResEntryName = null;
|
||||
}
|
||||
final long expiryTimeMillis = XmlUtils.readLongAttribute(in, ATTR_EXPIRY_TIME);
|
||||
final CharSequence description;
|
||||
if (version >= XML_VERSION_ADD_STRING_DESC) {
|
||||
@@ -520,7 +587,8 @@ class BlobMetadata {
|
||||
description = null;
|
||||
}
|
||||
|
||||
return new Leasee(packageName, uid, descriptionResId, description, expiryTimeMillis);
|
||||
return new Leasee(packageName, uid, descriptionResEntryName,
|
||||
description, expiryTimeMillis);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,8 +32,9 @@ class BlobStoreConfig {
|
||||
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_ADD_DESC_RES_NAME = 3;
|
||||
|
||||
public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_STRING_DESC;
|
||||
public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_DESC_RES_NAME;
|
||||
|
||||
private static final String ROOT_DIR_NAME = "blobstore";
|
||||
private static final String BLOBS_DIR_NAME = "blobs";
|
||||
|
||||
@@ -54,6 +54,7 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PackageStats;
|
||||
import android.content.res.ResourceId;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
@@ -1181,8 +1182,12 @@ public class BlobStoreManagerService extends SystemService {
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
verifyCallingPackage(callingUid, packageName);
|
||||
|
||||
acquireLeaseInternal(blobHandle, descriptionResId, description, leaseExpiryTimeMillis,
|
||||
callingUid, packageName);
|
||||
try {
|
||||
acquireLeaseInternal(blobHandle, descriptionResId, description,
|
||||
leaseExpiryTimeMillis, callingUid, packageName);
|
||||
} catch (Resources.NotFoundException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user