Merge "Keep track of commit times for each blob." into rvc-dev am: b78a0f462e

Change-Id: I240071a9fbdce84d63d0ff80b4bcbe1e3323b2b6
This commit is contained in:
Sudheer Shanka
2020-04-10 17:41:04 +00:00
committed by Automerger Merge Worker
6 changed files with 94 additions and 16 deletions

View File

@@ -48,6 +48,7 @@ public final class XmlTags {
// For committer // For committer
public static final String TAG_COMMITTER = "c"; public static final String TAG_COMMITTER = "c";
public static final String ATTR_COMMIT_TIME_MS = "cmt";
// For leasee // For leasee
public static final String TAG_LEASEE = "l"; public static final String TAG_LEASEE = "l";

View File

@@ -15,6 +15,7 @@
*/ */
package com.android.server.blob; package com.android.server.blob;
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;
import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME; import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
@@ -30,6 +31,7 @@ import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.O_RDONLY; import static android.system.OsConstants.O_RDONLY;
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_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.hasLeaseWaitTimeElapsed; import static com.android.server.blob.BlobStoreConfig.hasLeaseWaitTimeElapsed;
@@ -54,6 +56,7 @@ import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter; 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;
@@ -125,7 +128,7 @@ class BlobMetadata {
} }
} }
void addCommitters(ArraySet<Committer> committers) { void setCommitters(ArraySet<Committer> committers) {
synchronized (mMetadataLock) { synchronized (mMetadataLock) {
mCommitters.clear(); mCommitters.clear();
mCommitters.addAll(committers); mCommitters.addAll(committers);
@@ -153,12 +156,17 @@ class BlobMetadata {
} }
@Nullable @Nullable
Committer getExistingCommitter(@NonNull Committer newCommitter) { Committer getExistingCommitter(@NonNull String packageName, int uid) {
synchronized (mCommitters) { synchronized (mCommitters) {
final int index = mCommitters.indexOf(newCommitter); for (int i = 0, size = mCommitters.size(); i < size; ++i) {
return index >= 0 ? mCommitters.valueAt(index) : null; final Committer committer = mCommitters.valueAt(i);
if (committer.uid == uid && committer.packageName.equals(packageName)) {
return committer;
} }
} }
}
return null;
}
void addOrReplaceLeasee(String callingPackage, int callingUid, int descriptionResId, void addOrReplaceLeasee(String callingPackage, int callingUid, int descriptionResId,
CharSequence description, long leaseExpiryTimeMillis) { CharSequence description, long leaseExpiryTimeMillis) {
@@ -172,7 +180,7 @@ class BlobMetadata {
} }
} }
void addLeasees(ArraySet<Leasee> leasees) { void setLeasees(ArraySet<Leasee> leasees) {
synchronized (mMetadataLock) { synchronized (mMetadataLock) {
mLeasees.clear(); mLeasees.clear();
mLeasees.addAll(leasees); mLeasees.addAll(leasees);
@@ -380,8 +388,7 @@ class BlobMetadata {
} }
// Blobs with no active leases // Blobs with no active leases
// TODO: Track commit time instead of using last modified time. if ((!respectLeaseWaitTime || hasLeaseWaitTimeElapsedForAll())
if ((!respectLeaseWaitTime || hasLeaseWaitTimeElapsed(getBlobFile().lastModified()))
&& !hasLeases()) { && !hasLeases()) {
return true; return true;
} }
@@ -389,6 +396,17 @@ class BlobMetadata {
return false; return false;
} }
@VisibleForTesting
boolean hasLeaseWaitTimeElapsedForAll() {
for (int i = 0, size = mCommitters.size(); i < size; ++i) {
final Committer committer = mCommitters.valueAt(i);
if (!hasLeaseWaitTimeElapsed(committer.getCommitTimeMs())) {
return false;
}
}
return true;
}
void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) { void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
fout.println("blobHandle:"); fout.println("blobHandle:");
fout.increaseIndent(); fout.increaseIndent();
@@ -492,20 +510,28 @@ class BlobMetadata {
} }
final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle, userId); final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle, userId);
blobMetadata.addCommitters(committers); blobMetadata.setCommitters(committers);
blobMetadata.addLeasees(leasees); blobMetadata.setLeasees(leasees);
return blobMetadata; return blobMetadata;
} }
static final class Committer extends Accessor { static final class Committer extends Accessor {
public final BlobAccessMode blobAccessMode; public final BlobAccessMode blobAccessMode;
public final long commitTimeMs;
Committer(String packageName, int uid, BlobAccessMode blobAccessMode) { Committer(String packageName, int uid, BlobAccessMode blobAccessMode, long commitTimeMs) {
super(packageName, uid); super(packageName, uid);
this.blobAccessMode = blobAccessMode; this.blobAccessMode = blobAccessMode;
this.commitTimeMs = commitTimeMs;
}
long getCommitTimeMs() {
return commitTimeMs;
} }
void dump(IndentingPrintWriter fout) { void dump(IndentingPrintWriter fout) {
fout.println("commit time: "
+ (commitTimeMs == 0 ? "<null>" : BlobStoreUtils.formatTime(commitTimeMs)));
fout.println("accessMode:"); fout.println("accessMode:");
fout.increaseIndent(); fout.increaseIndent();
blobAccessMode.dump(fout); blobAccessMode.dump(fout);
@@ -515,6 +541,7 @@ class BlobMetadata {
void writeToXml(@NonNull XmlSerializer out) throws IOException { void writeToXml(@NonNull XmlSerializer out) throws IOException {
XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName); XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
XmlUtils.writeIntAttribute(out, ATTR_UID, uid); XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
XmlUtils.writeLongAttribute(out, ATTR_COMMIT_TIME_MS, commitTimeMs);
out.startTag(null, TAG_ACCESS_MODE); out.startTag(null, TAG_ACCESS_MODE);
blobAccessMode.writeToXml(out); blobAccessMode.writeToXml(out);
@@ -526,6 +553,9 @@ class BlobMetadata {
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE); final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
final int uid = XmlUtils.readIntAttribute(in, ATTR_UID); final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
final long commitTimeMs = version >= XML_VERSION_ADD_COMMIT_TIME
? XmlUtils.readLongAttribute(in, ATTR_COMMIT_TIME_MS)
: 0;
final int depth = in.getDepth(); final int depth = in.getDepth();
BlobAccessMode blobAccessMode = null; BlobAccessMode blobAccessMode = null;
@@ -538,7 +568,7 @@ class BlobMetadata {
Slog.wtf(TAG, "blobAccessMode should be available"); Slog.wtf(TAG, "blobAccessMode should be available");
return null; return null;
} }
return new Committer(packageName, uid, blobAccessMode); return new Committer(packageName, uid, blobAccessMode, commitTimeMs);
} }
} }

View File

@@ -45,8 +45,9 @@ class BlobStoreConfig {
// Added a string variant of lease description. // Added a string variant of lease description.
public static final int XML_VERSION_ADD_STRING_DESC = 2; 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_ADD_DESC_RES_NAME = 3;
public static final int XML_VERSION_ADD_COMMIT_TIME = 4;
public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_DESC_RES_NAME; public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_COMMIT_TIME;
private static final String ROOT_DIR_NAME = "blobstore"; private static final String ROOT_DIR_NAME = "blobstore";
private static final String BLOBS_DIR_NAME = "blobs"; private static final String BLOBS_DIR_NAME = "blobs";
@@ -100,6 +101,18 @@ class BlobStoreConfig {
public static long LEASE_ACQUISITION_WAIT_DURATION_MS = public static long LEASE_ACQUISITION_WAIT_DURATION_MS =
DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS; DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS;
/**
* Denotes the duration from the time a blob is committed that any new commits of the same
* data blob from the same committer will be treated as if they occurred at the earlier
* commit time.
*/
public static final String KEY_COMMIT_COOL_OFF_DURATION_MS =
"commit_cool_off_duration_ms";
public static final long DEFAULT_COMMIT_COOL_OFF_DURATION_MS =
TimeUnit.HOURS.toMillis(48);
public static long COMMIT_COOL_OFF_DURATION_MS =
DEFAULT_COMMIT_COOL_OFF_DURATION_MS;
static void refresh(Properties properties) { static void refresh(Properties properties) {
if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) { if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) {
return; return;
@@ -163,6 +176,27 @@ class BlobStoreConfig {
< System.currentTimeMillis(); < System.currentTimeMillis();
} }
/**
* Returns an adjusted commit time depending on whether commit cool-off period has elapsed.
*
* If this is the initial commit or the earlier commit cool-off period has elapsed, then
* the new commit time is used. Otherwise, the earlier commit time is used.
*/
public static long getAdjustedCommitTimeMs(long oldCommitTimeMs, long newCommitTimeMs) {
if (oldCommitTimeMs == 0 || hasCommitCoolOffPeriodElapsed(oldCommitTimeMs)) {
return newCommitTimeMs;
}
return oldCommitTimeMs;
}
/**
* Returns whether the commit cool-off period has elapsed.
*/
private static boolean hasCommitCoolOffPeriodElapsed(long commitTimeMs) {
return commitTimeMs + DeviceConfigProperties.COMMIT_COOL_OFF_DURATION_MS
< System.currentTimeMillis();
}
@Nullable @Nullable
public static File prepareBlobFile(long sessionId) { public static File prepareBlobFile(long sessionId) {
final File blobsDir = prepareBlobsDir(); final File blobsDir = prepareBlobsDir();

View File

@@ -31,6 +31,7 @@ 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.SESSION_EXPIRY_TIMEOUT_MILLIS;
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_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.BlobStoreSession.STATE_ABANDONED; 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_COMMITTED;
import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID; import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
@@ -566,13 +567,18 @@ public class BlobStoreManagerService extends SystemService {
userId); userId);
BlobMetadata blob = userBlobs.get(session.getBlobHandle()); BlobMetadata blob = userBlobs.get(session.getBlobHandle());
if (blob == null) { if (blob == null) {
blob = new BlobMetadata(mContext, blob = new BlobMetadata(mContext, session.getSessionId(),
session.getSessionId(), session.getBlobHandle(), userId); session.getBlobHandle(), userId);
addBlobForUserLocked(blob, userBlobs); addBlobForUserLocked(blob, userBlobs);
} }
final Committer existingCommitter = blob.getExistingCommitter(
session.getOwnerPackageName(), session.getOwnerUid());
final long existingCommitTimeMs =
(existingCommitter == null) ? 0 : existingCommitter.getCommitTimeMs();
final Committer newCommitter = new Committer(session.getOwnerPackageName(), final Committer newCommitter = new Committer(session.getOwnerPackageName(),
session.getOwnerUid(), session.getBlobAccessMode()); session.getOwnerUid(), session.getBlobAccessMode(),
final Committer existingCommitter = blob.getExistingCommitter(newCommitter); getAdjustedCommitTimeMs(existingCommitTimeMs,
System.currentTimeMillis()));
blob.addOrReplaceCommitter(newCommitter); blob.addOrReplaceCommitter(newCommitter);
try { try {
writeBlobsInfoLocked(); writeBlobsInfoLocked();

View File

@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.text.format.TimeMigrationUtils;
import android.util.Slog; import android.util.Slog;
class BlobStoreUtils { class BlobStoreUtils {
@@ -56,4 +57,9 @@ class BlobStoreUtils {
? Resources.ID_NULL ? Resources.ID_NULL
: getDescriptionResourceId(resources, resourceEntryName, packageName); : getDescriptionResourceId(resources, resourceEntryName, packageName);
} }
@NonNull
static String formatTime(long timeMs) {
return TimeMigrationUtils.formatMillisWithFixedFormat(timeMs);
}
} }

View File

@@ -382,6 +382,7 @@ public class BlobStoreManagerServiceTest {
doReturn(hasLeases).when(blobMetadata).hasLeases(); doReturn(hasLeases).when(blobMetadata).hasLeases();
doReturn(blobHandle).when(blobMetadata).getBlobHandle(); doReturn(blobHandle).when(blobMetadata).getBlobHandle();
doCallRealMethod().when(blobMetadata).shouldBeDeleted(anyBoolean()); doCallRealMethod().when(blobMetadata).shouldBeDeleted(anyBoolean());
doReturn(true).when(blobMetadata).hasLeaseWaitTimeElapsedForAll();
return blobMetadata; return blobMetadata;
} }