diff options
6 files changed, 76 insertions, 23 deletions
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 b4a7cd4de2ae..9a711d31b6a9 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java @@ -18,7 +18,6 @@ package com.android.server.blob; import static android.provider.DeviceConfig.NAMESPACE_BLOBSTORE; import static android.text.format.Formatter.FLAG_IEC_UNITS; import static android.text.format.Formatter.formatFileSize; -import static android.util.TimeUtils.formatDuration; import android.annotation.NonNull; import android.annotation.Nullable; @@ -58,18 +57,24 @@ class BlobStoreConfig { * Job Id for idle maintenance job ({@link BlobStoreIdleJobService}). */ public static final int IDLE_JOB_ID = 0xB70B1D7; // 191934935L - /** - * Max time period (in millis) between each idle maintenance job run. - */ - public static final long IDLE_JOB_PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1); - - /** - * Timeout in millis after which sessions with no updates will be deleted. - */ - public static final long SESSION_EXPIRY_TIMEOUT_MILLIS = TimeUnit.DAYS.toMillis(7); public static class DeviceConfigProperties { /** + * Denotes the max time period (in millis) between each idle maintenance job run. + */ + public static final String KEY_IDLE_JOB_PERIOD_MS = "idle_job_period_ms"; + public static final long DEFAULT_IDLE_JOB_PERIOD_MS = TimeUnit.DAYS.toMillis(1); + public static long IDLE_JOB_PERIOD_MS = DEFAULT_IDLE_JOB_PERIOD_MS; + + /** + * Denotes the timeout in millis after which sessions with no updates will be deleted. + */ + public static final String KEY_SESSION_EXPIRY_TIMEOUT_MS = + "session_expiry_timeout_ms"; + public static final long DEFAULT_SESSION_EXPIRY_TIMEOUT_MS = TimeUnit.DAYS.toMillis(7); + public static long SESSION_EXPIRY_TIMEOUT_MS = DEFAULT_SESSION_EXPIRY_TIMEOUT_MS; + + /** * Denotes how low the limit for the amount of data, that an app will be allowed to acquire * a lease on, can be. */ @@ -119,6 +124,13 @@ class BlobStoreConfig { } properties.getKeyset().forEach(key -> { switch (key) { + case KEY_IDLE_JOB_PERIOD_MS: + IDLE_JOB_PERIOD_MS = properties.getLong(key, DEFAULT_IDLE_JOB_PERIOD_MS); + break; + case KEY_SESSION_EXPIRY_TIMEOUT_MS: + SESSION_EXPIRY_TIMEOUT_MS = properties.getLong(key, + DEFAULT_SESSION_EXPIRY_TIMEOUT_MS); + break; case KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR: TOTAL_BYTES_PER_APP_LIMIT_FLOOR = properties.getLong(key, DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR); @@ -143,6 +155,12 @@ class BlobStoreConfig { static void dump(IndentingPrintWriter fout, Context context) { final String dumpFormat = "%s: [cur: %s, def: %s]"; + fout.println(String.format(dumpFormat, KEY_IDLE_JOB_PERIOD_MS, + TimeUtils.formatDuration(IDLE_JOB_PERIOD_MS), + TimeUtils.formatDuration(DEFAULT_IDLE_JOB_PERIOD_MS))); + fout.println(String.format(dumpFormat, KEY_SESSION_EXPIRY_TIMEOUT_MS, + TimeUtils.formatDuration(SESSION_EXPIRY_TIMEOUT_MS), + TimeUtils.formatDuration(DEFAULT_SESSION_EXPIRY_TIMEOUT_MS))); fout.println(String.format(dumpFormat, KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR, formatFileSize(context, TOTAL_BYTES_PER_APP_LIMIT_FLOOR, FLAG_IEC_UNITS), formatFileSize(context, DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR, @@ -167,6 +185,22 @@ class BlobStoreConfig { } /** + * Returns the max time period (in millis) between each idle maintenance job run. + */ + public static long getIdleJobPeriodMs() { + return DeviceConfigProperties.IDLE_JOB_PERIOD_MS; + } + + /** + * Returns whether a session is expired or not. A session is considered expired if the session + * has not been modified in a while (i.e. SESSION_EXPIRY_TIMEOUT_MS). + */ + public static boolean hasSessionExpired(long sessionLastModifiedMs) { + return sessionLastModifiedMs + < System.currentTimeMillis() - DeviceConfigProperties.SESSION_EXPIRY_TIMEOUT_MS; + } + + /** * Returns the maximum amount of data that an app can acquire a lease on. */ public static long getAppDataBytesLimit() { @@ -277,9 +311,6 @@ class BlobStoreConfig { fout.println("XML current version: " + XML_VERSION_CURRENT); fout.println("Idle job ID: " + IDLE_JOB_ID); - fout.println("Idle job period: " + formatDuration(IDLE_JOB_PERIOD_MILLIS)); - - fout.println("Session expiry timeout: " + formatDuration(SESSION_EXPIRY_TIMEOUT_MILLIS)); fout.println("Total bytes per app limit: " + formatFileSize(context, getAppDataBytesLimit(), FLAG_IEC_UNITS)); diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java index 460e776b9ff6..4b0f719b13be 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java @@ -16,7 +16,6 @@ package com.android.server.blob; import static com.android.server.blob.BlobStoreConfig.IDLE_JOB_ID; -import static com.android.server.blob.BlobStoreConfig.IDLE_JOB_PERIOD_MILLIS; import static com.android.server.blob.BlobStoreConfig.LOGV; import static com.android.server.blob.BlobStoreConfig.TAG; @@ -60,7 +59,7 @@ public class BlobStoreIdleJobService extends JobService { new ComponentName(context, BlobStoreIdleJobService.class)) .setRequiresDeviceIdle(true) .setRequiresCharging(true) - .setPeriodic(IDLE_JOB_PERIOD_MILLIS) + .setPeriodic(BlobStoreConfig.getIdleJobPeriodMs()) .build(); jobScheduler.schedule(job); if (LOGV) { 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 9376198b8eaf..7a27b2c795fb 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -29,10 +29,10 @@ import static android.os.UserHandle.USER_CURRENT; import static android.os.UserHandle.USER_NULL; 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.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 +986,9 @@ public class BlobStoreManagerService extends SystemService { 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 (blobStoreSession.getSessionFile().lastModified() - < System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS) { + if (hasSessionExpired(blobStoreSession.getSessionFile().lastModified())) { shouldRemove = true; } 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 d338b587e059..ade01dc6afa6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java @@ -19,7 +19,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealM import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.server.blob.BlobStoreConfig.SESSION_EXPIRY_TIMEOUT_MILLIS; +import static com.android.server.blob.BlobStoreConfig.DeviceConfigProperties.SESSION_EXPIRY_TIMEOUT_MS; import static com.google.common.truth.Truth.assertThat; @@ -93,6 +93,7 @@ public class BlobStoreManagerServiceTest { doReturn(true).when(mBlobsDir).exists(); doReturn(new File[0]).when(mBlobsDir).listFiles(); doReturn(true).when(() -> BlobStoreConfig.hasLeaseWaitTimeElapsed(anyLong())); + doCallRealMethod().when(() -> BlobStoreConfig.hasSessionExpired(anyLong())); mContext = InstrumentationRegistry.getTargetContext(); mHandler = new TestHandler(Looper.getMainLooper()); @@ -236,7 +237,7 @@ public class BlobStoreManagerServiceTest { public void testHandleIdleMaintenance_deleteStaleSessions() throws Exception { // Setup sessions final File sessionFile1 = mock(File.class); - doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS + 1000) + doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MS + 1000) .when(sessionFile1).lastModified(); final long sessionId1 = 342; final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(), @@ -256,7 +257,7 @@ public class BlobStoreManagerServiceTest { mUserSessions.append(sessionId2, session2); final File sessionFile3 = mock(File.class); - doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS - 2000) + doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MS - 2000) .when(sessionFile3).lastModified(); final long sessionId3 = 9484; final BlobHandle blobHandle3 = BlobHandle.createWithSha256("digest3".getBytes(), diff --git a/tests/BlobStoreTestUtils/Android.bp b/tests/BlobStoreTestUtils/Android.bp index 829c883913e1..5c7c68b57ba1 100644 --- a/tests/BlobStoreTestUtils/Android.bp +++ b/tests/BlobStoreTestUtils/Android.bp @@ -15,6 +15,9 @@ java_library { name: "BlobStoreTestUtils", srcs: ["src/**/*.java"], - static_libs: ["truth-prebuilt"], + static_libs: [ + "truth-prebuilt", + "androidx.test.uiautomator_uiautomator", + ], sdk_version: "test_current", }
\ No newline at end of file diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java index 6927e86213d8..7cf58e1682bf 100644 --- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java +++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java @@ -18,12 +18,16 @@ package com.android.utils.blob; import static com.google.common.truth.Truth.assertThat; +import android.app.Instrumentation; import android.app.blob.BlobHandle; import android.app.blob.BlobStoreManager; import android.app.blob.LeaseInfo; import android.content.Context; import android.content.res.Resources; import android.os.ParcelFileDescriptor; +import android.util.Log; + +import androidx.test.uiautomator.UiDevice; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -32,6 +36,8 @@ import java.io.InputStream; import java.io.OutputStream; public class Utils { + public static final String TAG = "BlobStoreTest"; + public static final int BUFFER_SIZE_BYTES = 16 * 1024; public static final long KB_IN_BYTES = 1000; @@ -68,7 +74,8 @@ public class Utils { public static void assertLeasedBlobs(BlobStoreManager blobStoreManager, BlobHandle... expectedBlobHandles) throws IOException { - assertThat(blobStoreManager.getLeasedBlobs()).containsExactly(expectedBlobHandles); + assertThat(blobStoreManager.getLeasedBlobs()).containsExactly( + (Object[]) expectedBlobHandles); } public static void assertNoLeasedBlobs(BlobStoreManager blobStoreManager) @@ -141,4 +148,16 @@ public class Utils { assertThat(leaseInfo.getDescriptionResId()).isEqualTo(descriptionResId); assertThat(leaseInfo.getDescription()).isEqualTo(description); } + + public static void triggerIdleMaintenance(Instrumentation instrumentation) throws IOException { + runShellCmd(instrumentation, "cmd blob_store idle-maintenance"); + } + + private static String runShellCmd(Instrumentation instrumentation, + String cmd) throws IOException { + final UiDevice uiDevice = UiDevice.getInstance(instrumentation); + final String result = uiDevice.executeShellCommand(cmd); + Log.i(TAG, "Output of '" + cmd + "': '" + result + "'"); + return result; + } } |