Persist blobstore session creation time.

This can be used for checking if a session
should be considered expired and needs to
be deleted.

Bug: 147722548
Test: atest --test-mapping apex/blobstore
Change-Id: I0f500b2baf71f8104a0a6c233071261f9ba9cc14
diff --git a/apex/blobstore/framework/java/android/app/blob/XmlTags.java b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
index dbfdcba..656749d 100644
--- a/apex/blobstore/framework/java/android/app/blob/XmlTags.java
+++ b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
@@ -27,6 +27,7 @@
     public static final String ATTR_ID = "id";
     public static final String ATTR_PACKAGE = "p";
     public static final String ATTR_UID = "u";
+    public static final String ATTR_CREATION_TIME_MS = "crt";
 
     // For BlobMetadata
     public static final String TAG_BLOB = "b";
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 9a711d3..6567266 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 @@
     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_ADD_SESSION_CREATION_TIME = 5;
 
-    public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_COMMIT_TIME;
+    public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_SESSION_CREATION_TIME;
 
     private static final String ROOT_DIR_NAME = "blobstore";
     private static final String BLOBS_DIR_NAME = "blobs";
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 a2bce31..8fff0d9 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -32,7 +32,6 @@
 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.BlobStoreConfig.hasSessionExpired;
 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;
@@ -986,9 +985,8 @@
             userSessions.removeIf((sessionId, blobStoreSession) -> {
                 boolean shouldRemove = false;
 
-                // TODO: handle the case where no content has been written to session yet.
                 // Cleanup sessions which haven't been modified in a while.
-                if (hasSessionExpired(blobStoreSession.getSessionFile().lastModified())) {
+                if (blobStoreSession.isExpired()) {
                     shouldRemove = true;
                 }
 
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 62701e5..2b04583 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -16,6 +16,7 @@
 package com.android.server.blob;
 
 import static android.app.blob.BlobStoreManager.COMMIT_RESULT_ERROR;
+import static android.app.blob.XmlTags.ATTR_CREATION_TIME_MS;
 import static android.app.blob.XmlTags.ATTR_ID;
 import static android.app.blob.XmlTags.ATTR_PACKAGE;
 import static android.app.blob.XmlTags.ATTR_UID;
@@ -29,6 +30,8 @@
 
 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_SESSION_CREATION_TIME;
+import static com.android.server.blob.BlobStoreConfig.hasSessionExpired;
 
 import android.annotation.BytesLong;
 import android.annotation.NonNull;
@@ -89,6 +92,7 @@
     private final long mSessionId;
     private final int mOwnerUid;
     private final String mOwnerPackageName;
+    private final long mCreationTimeMs;
 
     // Do not access this directly, instead use getSessionFile().
     private File mSessionFile;
@@ -109,16 +113,24 @@
     @GuardedBy("mSessionLock")
     private IBlobCommitCallback mBlobCommitCallback;
 
-    BlobStoreSession(Context context, long sessionId, BlobHandle blobHandle,
-            int ownerUid, String ownerPackageName, SessionStateChangeListener listener) {
+    private BlobStoreSession(Context context, long sessionId, BlobHandle blobHandle,
+            int ownerUid, String ownerPackageName, long creationTimeMs,
+            SessionStateChangeListener listener) {
         this.mContext = context;
         this.mBlobHandle = blobHandle;
         this.mSessionId = sessionId;
         this.mOwnerUid = ownerUid;
         this.mOwnerPackageName = ownerPackageName;
+        this.mCreationTimeMs = creationTimeMs;
         this.mListener = listener;
     }
 
+    BlobStoreSession(Context context, long sessionId, BlobHandle blobHandle,
+            int ownerUid, String ownerPackageName, SessionStateChangeListener listener) {
+        this(context, sessionId, blobHandle, ownerUid, ownerPackageName,
+                System.currentTimeMillis(), listener);
+    }
+
     public BlobHandle getBlobHandle() {
         return mBlobHandle;
     }
@@ -178,6 +190,12 @@
         }
     }
 
+    boolean isExpired() {
+        final long lastModifiedTimeMs = getSessionFile().lastModified();
+        return hasSessionExpired(lastModifiedTimeMs == 0
+                ? mCreationTimeMs : lastModifiedTimeMs);
+    }
+
     @Override
     @NonNull
     public ParcelFileDescriptor openWrite(@BytesLong long offsetBytes,
@@ -491,6 +509,7 @@
             fout.println("state: " + stateToString(mState));
             fout.println("ownerUid: " + mOwnerUid);
             fout.println("ownerPkg: " + mOwnerPackageName);
+            fout.println("creation time: " + BlobStoreUtils.formatTime(mCreationTimeMs));
 
             fout.println("blobHandle:");
             fout.increaseIndent();
@@ -511,6 +530,7 @@
             XmlUtils.writeLongAttribute(out, ATTR_ID, mSessionId);
             XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, mOwnerPackageName);
             XmlUtils.writeIntAttribute(out, ATTR_UID, mOwnerUid);
+            XmlUtils.writeLongAttribute(out, ATTR_CREATION_TIME_MS, mCreationTimeMs);
 
             out.startTag(null, TAG_BLOB_HANDLE);
             mBlobHandle.writeToXml(out);
@@ -529,6 +549,9 @@
         final long sessionId = XmlUtils.readLongAttribute(in, ATTR_ID);
         final String ownerPackageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
         final int ownerUid = XmlUtils.readIntAttribute(in, ATTR_UID);
+        final long creationTimeMs = version >= XML_VERSION_ADD_SESSION_CREATION_TIME
+                ? XmlUtils.readLongAttribute(in, ATTR_CREATION_TIME_MS)
+                : System.currentTimeMillis();
 
         final int depth = in.getDepth();
         BlobHandle blobHandle = null;
@@ -551,7 +574,7 @@
         }
 
         final BlobStoreSession blobStoreSession = new BlobStoreSession(context, sessionId,
-                blobHandle, ownerUid, ownerPackageName, stateChangeListener);
+                blobHandle, ownerUid, ownerPackageName, creationTimeMs, stateChangeListener);
         blobStoreSession.mBlobAccessMode.allow(blobAccessMode);
         return blobStoreSession;
     }
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 ade01dc..7446289 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -372,6 +372,7 @@
         doReturn(sessionId).when(session).getSessionId();
         doReturn(sessionFile).when(session).getSessionFile();
         doReturn(blobHandle).when(session).getBlobHandle();
+        doCallRealMethod().when(session).isExpired();
         return session;
     }