Merge "Update a couple of blobstore params to be configurable." into rvc-dev am: 61a459d77a
Change-Id: Ie94a20341f107dee478042deb18a00c51868f928
This commit is contained in:
@@ -18,7 +18,6 @@ package com.android.server.blob;
|
|||||||
import static android.provider.DeviceConfig.NAMESPACE_BLOBSTORE;
|
import static android.provider.DeviceConfig.NAMESPACE_BLOBSTORE;
|
||||||
import static android.text.format.Formatter.FLAG_IEC_UNITS;
|
import static android.text.format.Formatter.FLAG_IEC_UNITS;
|
||||||
import static android.text.format.Formatter.formatFileSize;
|
import static android.text.format.Formatter.formatFileSize;
|
||||||
import static android.util.TimeUtils.formatDuration;
|
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
@@ -58,17 +57,23 @@ class BlobStoreConfig {
|
|||||||
* Job Id for idle maintenance job ({@link BlobStoreIdleJobService}).
|
* Job Id for idle maintenance job ({@link BlobStoreIdleJobService}).
|
||||||
*/
|
*/
|
||||||
public static final int IDLE_JOB_ID = 0xB70B1D7; // 191934935L
|
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 {
|
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
|
* Denotes how low the limit for the amount of data, that an app will be allowed to acquire
|
||||||
* a lease on, can be.
|
* a lease on, can be.
|
||||||
@@ -119,6 +124,13 @@ class BlobStoreConfig {
|
|||||||
}
|
}
|
||||||
properties.getKeyset().forEach(key -> {
|
properties.getKeyset().forEach(key -> {
|
||||||
switch (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:
|
case KEY_TOTAL_BYTES_PER_APP_LIMIT_FLOOR:
|
||||||
TOTAL_BYTES_PER_APP_LIMIT_FLOOR = properties.getLong(key,
|
TOTAL_BYTES_PER_APP_LIMIT_FLOOR = properties.getLong(key,
|
||||||
DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR);
|
DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR);
|
||||||
@@ -143,6 +155,12 @@ class BlobStoreConfig {
|
|||||||
|
|
||||||
static void dump(IndentingPrintWriter fout, Context context) {
|
static void dump(IndentingPrintWriter fout, Context context) {
|
||||||
final String dumpFormat = "%s: [cur: %s, def: %s]";
|
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,
|
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, TOTAL_BYTES_PER_APP_LIMIT_FLOOR, FLAG_IEC_UNITS),
|
||||||
formatFileSize(context, DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR,
|
formatFileSize(context, DEFAULT_TOTAL_BYTES_PER_APP_LIMIT_FLOOR,
|
||||||
@@ -166,6 +184,22 @@ class BlobStoreConfig {
|
|||||||
DeviceConfigProperties.refresh(DeviceConfig.getProperties(NAMESPACE_BLOBSTORE));
|
DeviceConfigProperties.refresh(DeviceConfig.getProperties(NAMESPACE_BLOBSTORE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Returns the maximum amount of data that an app can acquire a lease on.
|
||||||
*/
|
*/
|
||||||
@@ -277,9 +311,6 @@ class BlobStoreConfig {
|
|||||||
fout.println("XML current version: " + XML_VERSION_CURRENT);
|
fout.println("XML current version: " + XML_VERSION_CURRENT);
|
||||||
|
|
||||||
fout.println("Idle job ID: " + IDLE_JOB_ID);
|
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,
|
fout.println("Total bytes per app limit: " + formatFileSize(context,
|
||||||
getAppDataBytesLimit(), FLAG_IEC_UNITS));
|
getAppDataBytesLimit(), FLAG_IEC_UNITS));
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
package com.android.server.blob;
|
package com.android.server.blob;
|
||||||
|
|
||||||
import static com.android.server.blob.BlobStoreConfig.IDLE_JOB_ID;
|
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.LOGV;
|
||||||
import static com.android.server.blob.BlobStoreConfig.TAG;
|
import static com.android.server.blob.BlobStoreConfig.TAG;
|
||||||
|
|
||||||
@@ -60,7 +59,7 @@ public class BlobStoreIdleJobService extends JobService {
|
|||||||
new ComponentName(context, BlobStoreIdleJobService.class))
|
new ComponentName(context, BlobStoreIdleJobService.class))
|
||||||
.setRequiresDeviceIdle(true)
|
.setRequiresDeviceIdle(true)
|
||||||
.setRequiresCharging(true)
|
.setRequiresCharging(true)
|
||||||
.setPeriodic(IDLE_JOB_PERIOD_MILLIS)
|
.setPeriodic(BlobStoreConfig.getIdleJobPeriodMs())
|
||||||
.build();
|
.build();
|
||||||
jobScheduler.schedule(job);
|
jobScheduler.schedule(job);
|
||||||
if (LOGV) {
|
if (LOGV) {
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ import static android.os.UserHandle.USER_CURRENT;
|
|||||||
import static android.os.UserHandle.USER_NULL;
|
import static android.os.UserHandle.USER_NULL;
|
||||||
|
|
||||||
import static com.android.server.blob.BlobStoreConfig.LOGV;
|
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.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.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_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;
|
||||||
@@ -986,9 +986,9 @@ public class BlobStoreManagerService extends SystemService {
|
|||||||
userSessions.removeIf((sessionId, blobStoreSession) -> {
|
userSessions.removeIf((sessionId, blobStoreSession) -> {
|
||||||
boolean shouldRemove = false;
|
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.
|
// Cleanup sessions which haven't been modified in a while.
|
||||||
if (blobStoreSession.getSessionFile().lastModified()
|
if (hasSessionExpired(blobStoreSession.getSessionFile().lastModified())) {
|
||||||
< System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS) {
|
|
||||||
shouldRemove = true;
|
shouldRemove = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.doReturn;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
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;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
@@ -93,6 +93,7 @@ public class BlobStoreManagerServiceTest {
|
|||||||
doReturn(true).when(mBlobsDir).exists();
|
doReturn(true).when(mBlobsDir).exists();
|
||||||
doReturn(new File[0]).when(mBlobsDir).listFiles();
|
doReturn(new File[0]).when(mBlobsDir).listFiles();
|
||||||
doReturn(true).when(() -> BlobStoreConfig.hasLeaseWaitTimeElapsed(anyLong()));
|
doReturn(true).when(() -> BlobStoreConfig.hasLeaseWaitTimeElapsed(anyLong()));
|
||||||
|
doCallRealMethod().when(() -> BlobStoreConfig.hasSessionExpired(anyLong()));
|
||||||
|
|
||||||
mContext = InstrumentationRegistry.getTargetContext();
|
mContext = InstrumentationRegistry.getTargetContext();
|
||||||
mHandler = new TestHandler(Looper.getMainLooper());
|
mHandler = new TestHandler(Looper.getMainLooper());
|
||||||
@@ -236,7 +237,7 @@ public class BlobStoreManagerServiceTest {
|
|||||||
public void testHandleIdleMaintenance_deleteStaleSessions() throws Exception {
|
public void testHandleIdleMaintenance_deleteStaleSessions() throws Exception {
|
||||||
// Setup sessions
|
// Setup sessions
|
||||||
final File sessionFile1 = mock(File.class);
|
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();
|
.when(sessionFile1).lastModified();
|
||||||
final long sessionId1 = 342;
|
final long sessionId1 = 342;
|
||||||
final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
|
final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
|
||||||
@@ -256,7 +257,7 @@ public class BlobStoreManagerServiceTest {
|
|||||||
mUserSessions.append(sessionId2, session2);
|
mUserSessions.append(sessionId2, session2);
|
||||||
|
|
||||||
final File sessionFile3 = mock(File.class);
|
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();
|
.when(sessionFile3).lastModified();
|
||||||
final long sessionId3 = 9484;
|
final long sessionId3 = 9484;
|
||||||
final BlobHandle blobHandle3 = BlobHandle.createWithSha256("digest3".getBytes(),
|
final BlobHandle blobHandle3 = BlobHandle.createWithSha256("digest3".getBytes(),
|
||||||
|
|||||||
@@ -15,6 +15,9 @@
|
|||||||
java_library {
|
java_library {
|
||||||
name: "BlobStoreTestUtils",
|
name: "BlobStoreTestUtils",
|
||||||
srcs: ["src/**/*.java"],
|
srcs: ["src/**/*.java"],
|
||||||
static_libs: ["truth-prebuilt"],
|
static_libs: [
|
||||||
|
"truth-prebuilt",
|
||||||
|
"androidx.test.uiautomator_uiautomator",
|
||||||
|
],
|
||||||
sdk_version: "test_current",
|
sdk_version: "test_current",
|
||||||
}
|
}
|
||||||
@@ -18,12 +18,16 @@ package com.android.utils.blob;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.app.Instrumentation;
|
||||||
import android.app.blob.BlobHandle;
|
import android.app.blob.BlobHandle;
|
||||||
import android.app.blob.BlobStoreManager;
|
import android.app.blob.BlobStoreManager;
|
||||||
import android.app.blob.LeaseInfo;
|
import android.app.blob.LeaseInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.test.uiautomator.UiDevice;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@@ -32,6 +36,8 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
public static final String TAG = "BlobStoreTest";
|
||||||
|
|
||||||
public static final int BUFFER_SIZE_BYTES = 16 * 1024;
|
public static final int BUFFER_SIZE_BYTES = 16 * 1024;
|
||||||
|
|
||||||
public static final long KB_IN_BYTES = 1000;
|
public static final long KB_IN_BYTES = 1000;
|
||||||
@@ -68,7 +74,8 @@ public class Utils {
|
|||||||
|
|
||||||
public static void assertLeasedBlobs(BlobStoreManager blobStoreManager,
|
public static void assertLeasedBlobs(BlobStoreManager blobStoreManager,
|
||||||
BlobHandle... expectedBlobHandles) throws IOException {
|
BlobHandle... expectedBlobHandles) throws IOException {
|
||||||
assertThat(blobStoreManager.getLeasedBlobs()).containsExactly(expectedBlobHandles);
|
assertThat(blobStoreManager.getLeasedBlobs()).containsExactly(
|
||||||
|
(Object[]) expectedBlobHandles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void assertNoLeasedBlobs(BlobStoreManager blobStoreManager)
|
public static void assertNoLeasedBlobs(BlobStoreManager blobStoreManager)
|
||||||
@@ -141,4 +148,16 @@ public class Utils {
|
|||||||
assertThat(leaseInfo.getDescriptionResId()).isEqualTo(descriptionResId);
|
assertThat(leaseInfo.getDescriptionResId()).isEqualTo(descriptionResId);
|
||||||
assertThat(leaseInfo.getDescription()).isEqualTo(description);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user