Merge "Keep track of commit times for each blob." into rvc-dev
diff --git a/apex/blobstore/framework/java/android/app/blob/XmlTags.java b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
index e64edc3..dbfdcba 100644
--- a/apex/blobstore/framework/java/android/app/blob/XmlTags.java
+++ b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
@@ -48,6 +48,7 @@
// For committer
public static final String TAG_COMMITTER = "c";
+ public static final String ATTR_COMMIT_TIME_MS = "cmt";
// For leasee
public static final String TAG_LEASEE = "l";
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index c8ca44b..49b3ec1 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -15,6 +15,7 @@
*/
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_RES_NAME;
import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
@@ -30,6 +31,7 @@
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_COMMIT_TIME;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_DESC_RES_NAME;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC;
import static com.android.server.blob.BlobStoreConfig.hasLeaseWaitTimeElapsed;
@@ -54,6 +56,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.server.blob.BlobStoreManagerService.DumpArgs;
@@ -125,7 +128,7 @@
}
}
- void addCommitters(ArraySet<Committer> committers) {
+ void setCommitters(ArraySet<Committer> committers) {
synchronized (mMetadataLock) {
mCommitters.clear();
mCommitters.addAll(committers);
@@ -153,11 +156,16 @@
}
@Nullable
- Committer getExistingCommitter(@NonNull Committer newCommitter) {
+ Committer getExistingCommitter(@NonNull String packageName, int uid) {
synchronized (mCommitters) {
- final int index = mCommitters.indexOf(newCommitter);
- return index >= 0 ? mCommitters.valueAt(index) : null;
+ for (int i = 0, size = mCommitters.size(); i < size; ++i) {
+ 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,
@@ -172,7 +180,7 @@
}
}
- void addLeasees(ArraySet<Leasee> leasees) {
+ void setLeasees(ArraySet<Leasee> leasees) {
synchronized (mMetadataLock) {
mLeasees.clear();
mLeasees.addAll(leasees);
@@ -380,8 +388,7 @@
}
// Blobs with no active leases
- // TODO: Track commit time instead of using last modified time.
- if ((!respectLeaseWaitTime || hasLeaseWaitTimeElapsed(getBlobFile().lastModified()))
+ if ((!respectLeaseWaitTime || hasLeaseWaitTimeElapsedForAll())
&& !hasLeases()) {
return true;
}
@@ -389,6 +396,17 @@
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) {
fout.println("blobHandle:");
fout.increaseIndent();
@@ -492,20 +510,28 @@
}
final BlobMetadata blobMetadata = new BlobMetadata(context, blobId, blobHandle, userId);
- blobMetadata.addCommitters(committers);
- blobMetadata.addLeasees(leasees);
+ blobMetadata.setCommitters(committers);
+ blobMetadata.setLeasees(leasees);
return blobMetadata;
}
static final class Committer extends Accessor {
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);
this.blobAccessMode = blobAccessMode;
+ this.commitTimeMs = commitTimeMs;
+ }
+
+ long getCommitTimeMs() {
+ return commitTimeMs;
}
void dump(IndentingPrintWriter fout) {
+ fout.println("commit time: "
+ + (commitTimeMs == 0 ? "<null>" : BlobStoreUtils.formatTime(commitTimeMs)));
fout.println("accessMode:");
fout.increaseIndent();
blobAccessMode.dump(fout);
@@ -515,6 +541,7 @@
void writeToXml(@NonNull XmlSerializer out) throws IOException {
XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
+ XmlUtils.writeLongAttribute(out, ATTR_COMMIT_TIME_MS, commitTimeMs);
out.startTag(null, TAG_ACCESS_MODE);
blobAccessMode.writeToXml(out);
@@ -526,6 +553,9 @@
throws XmlPullParserException, IOException {
final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
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();
BlobAccessMode blobAccessMode = null;
@@ -538,7 +568,7 @@
Slog.wtf(TAG, "blobAccessMode should be available");
return null;
}
- return new Committer(packageName, uid, blobAccessMode);
+ return new Committer(packageName, uid, blobAccessMode, commitTimeMs);
}
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index f2c1586..6af1178 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -45,8 +45,9 @@
// 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_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 BLOBS_DIR_NAME = "blobs";
@@ -100,6 +101,18 @@
public static long 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) {
if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) {
return;
@@ -163,6 +176,27 @@
< 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
public static File prepareBlobFile(long sessionId) {
final File blobsDir = prepareBlobsDir();
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index e472d05..35a2436 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -31,6 +31,7 @@
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.BlobStoreConfig.getAdjustedCommitTimeMs;
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;
@@ -566,13 +567,18 @@
userId);
BlobMetadata blob = userBlobs.get(session.getBlobHandle());
if (blob == null) {
- blob = new BlobMetadata(mContext,
- session.getSessionId(), session.getBlobHandle(), userId);
+ blob = new BlobMetadata(mContext, session.getSessionId(),
+ session.getBlobHandle(), userId);
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(),
- session.getOwnerUid(), session.getBlobAccessMode());
- final Committer existingCommitter = blob.getExistingCommitter(newCommitter);
+ session.getOwnerUid(), session.getBlobAccessMode(),
+ getAdjustedCommitTimeMs(existingCommitTimeMs,
+ System.currentTimeMillis()));
blob.addOrReplaceCommitter(newCommitter);
try {
writeBlobsInfoLocked();
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
index fabce76..1d07e88 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.text.format.TimeMigrationUtils;
import android.util.Slog;
class BlobStoreUtils {
@@ -56,4 +57,9 @@
? Resources.ID_NULL
: getDescriptionResourceId(resources, resourceEntryName, packageName);
}
+
+ @NonNull
+ static String formatTime(long timeMs) {
+ return TimeMigrationUtils.formatMillisWithFixedFormat(timeMs);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index f4d7b8b..d338b58 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -382,6 +382,7 @@
doReturn(hasLeases).when(blobMetadata).hasLeases();
doReturn(blobHandle).when(blobMetadata).getBlobHandle();
doCallRealMethod().when(blobMetadata).shouldBeDeleted(anyBoolean());
+ doReturn(true).when(blobMetadata).hasLeaseWaitTimeElapsedForAll();
return blobMetadata;
}