Add logging of the log list timestamp
This CL adds the parsed log list timestamp from the JSON log list, as
well as logging a SUCCESS event (with timestamp and signature) when the
log list has been successfully updated.
Fixes: 391327942
Fixes: 394278886
Bug: 378626065
Flag: com.android.net.ct.flags.certificate_transparency_service
Test: atest NetworkSecurityUnitTests
Change-Id: I8f0d96ca96cd89dce5da588cb478f9332537df48
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
index 1fbb3f3..fb42c03 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
@@ -199,7 +199,6 @@
}
LogListUpdateStatus updateStatus = mSignatureVerifier.verify(contentUri, metadataUri);
- // TODO(b/391327942): parse file and log the timestamp of the log list
if (!updateStatus.isSignatureVerified()) {
Log.w(TAG, "Log list did not pass verification");
@@ -209,42 +208,30 @@
return;
}
- boolean success = false;
-
try (InputStream inputStream = mContext.getContentResolver().openInputStream(contentUri)) {
- success = compatVersion.install(inputStream);
+ updateStatus = compatVersion.install(inputStream, updateStatus.toBuilder());
} catch (IOException e) {
Log.e(TAG, "Could not install new content", e);
return;
}
- if (success) {
- // Reset the number of consecutive log list failure updates back to zero.
- mDataStore.setPropertyInt(Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* value= */ 0);
- mDataStore.store();
- } else {
- mLogger.logCTLogListUpdateStateChangedEvent(
- updateStatus
- .toBuilder()
- .setState(CTLogListUpdateState.VERSION_ALREADY_EXISTS)
- .build());
- }
- }
+ mLogger.logCTLogListUpdateStateChangedEvent(updateStatus);
+ }
private void handleDownloadFailed(DownloadStatus status) {
Log.e(TAG, "Download failed with " + status);
- LogListUpdateStatus.Builder updateStatus = LogListUpdateStatus.builder();
+ LogListUpdateStatus.Builder updateStatusBuilder = LogListUpdateStatus.builder();
if (status.isHttpError()) {
- updateStatus
+ updateStatusBuilder
.setState(CTLogListUpdateState.HTTP_ERROR)
.setHttpErrorStatusCode(status.reason());
} else {
// TODO(b/384935059): handle blocked domain logging
- updateStatus.setDownloadStatus(Optional.of(status.reason()));
+ updateStatusBuilder.setDownloadStatus(Optional.of(status.reason()));
}
- mLogger.logCTLogListUpdateStateChangedEvent(updateStatus.build());
+ mLogger.logCTLogListUpdateStateChangedEvent(updateStatusBuilder.build());
}
private long download(String url) {
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java
index 967a04b..2a37d8f 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLogger.java
@@ -35,10 +35,12 @@
enum CTLogListUpdateState {
UNKNOWN_STATE,
HTTP_ERROR,
+ LOG_LIST_INVALID,
PUBLIC_KEY_NOT_FOUND,
SIGNATURE_INVALID,
SIGNATURE_NOT_FOUND,
SIGNATURE_VERIFICATION_FAILED,
+ SUCCESS,
VERSION_ALREADY_EXISTS
}
}
\ No newline at end of file
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java
index 9c3210d..f617523 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyLoggerImpl.java
@@ -20,6 +20,7 @@
import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_DEVICE_OFFLINE;
import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_DOWNLOAD_CANNOT_RESUME;
import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_HTTP_ERROR;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_LOG_LIST_INVALID;
import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_NO_DISK_SPACE;
import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_PUBLIC_KEY_NOT_FOUND;
import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_INVALID;
@@ -29,6 +30,7 @@
import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_UNKNOWN;
import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_VERSION_ALREADY_EXISTS;
import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__PENDING_WAITING_FOR_WIFI;
+import static com.android.server.net.ct.CertificateTransparencyStatsLog.CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__SUCCESS;
import android.app.DownloadManager;
@@ -43,6 +45,12 @@
@Override
public void logCTLogListUpdateStateChangedEvent(LogListUpdateStatus updateStatus) {
+ if (updateStatus.isSuccessful()) {
+ resetFailureCount();
+ } else {
+ updateFailureCount();
+ }
+
int updateState =
updateStatus
.downloadStatus()
@@ -56,20 +64,31 @@
updateState,
failureCount,
updateStatus.httpErrorStatusCode(),
- updateStatus.signature());
+ updateStatus.signature(),
+ updateStatus.logListTimestamp());
}
private void logCTLogListUpdateStateChangedEvent(
- int updateState, int failureCount, int httpErrorStatusCode, String signature) {
- updateFailureCount();
-
+ int updateState,
+ int failureCount,
+ int httpErrorStatusCode,
+ String signature,
+ long logListTimestamp) {
CertificateTransparencyStatsLog.write(
CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED,
updateState,
failureCount,
httpErrorStatusCode,
signature,
- /* logListTimestampMs= */ 0);
+ logListTimestamp);
+ }
+
+ /**
+ * Resets the number of consecutive log list update failures in the data store back to zero.
+ */
+ private void resetFailureCount() {
+ mDataStore.setPropertyInt(Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* value= */ 0);
+ mDataStore.store();
}
/**
@@ -112,6 +131,8 @@
switch (updateState) {
case HTTP_ERROR:
return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_HTTP_ERROR;
+ case LOG_LIST_INVALID:
+ return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_LOG_LIST_INVALID;
case PUBLIC_KEY_NOT_FOUND:
return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_PUBLIC_KEY_NOT_FOUND;
case SIGNATURE_INVALID:
@@ -120,6 +141,8 @@
return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_NOT_FOUND;
case SIGNATURE_VERIFICATION_FAILED:
return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_SIGNATURE_VERIFICATION;
+ case SUCCESS:
+ return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__SUCCESS;
case VERSION_ALREADY_EXISTS:
return CERTIFICATE_TRANSPARENCY_LOG_LIST_UPDATE_STATE_CHANGED__UPDATE_STATUS__FAILURE_VERSION_ALREADY_EXISTS;
case UNKNOWN_STATE:
diff --git a/networksecurity/service/src/com/android/server/net/ct/CompatibilityVersion.java b/networksecurity/service/src/com/android/server/net/ct/CompatibilityVersion.java
index 9d60163..e8a6e64 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CompatibilityVersion.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CompatibilityVersion.java
@@ -23,6 +23,8 @@
import android.system.Os;
import android.util.Log;
+import com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -69,22 +71,29 @@
* Installs a log list within this compatibility version directory.
*
* @param newContent an input stream providing the log list
+ * @param statusBuilder status obj builder containing details of the log list update process
* @return true if the log list was installed successfully, false otherwise.
* @throws IOException if the list cannot be saved in the CT directory.
*/
- boolean install(InputStream newContent) throws IOException {
+ LogListUpdateStatus install(
+ InputStream newContent, LogListUpdateStatus.Builder statusBuilder) throws IOException {
String content = new String(newContent.readAllBytes(), UTF_8);
try {
+ JSONObject contentJson = new JSONObject(content);
return install(
new ByteArrayInputStream(content.getBytes()),
- new JSONObject(content).getString("version"));
+ contentJson.getString("version"),
+ statusBuilder.setLogListTimestamp(contentJson.getLong("log_list_timestamp")));
} catch (JSONException e) {
Log.e(TAG, "invalid log list format", e);
- return false;
+
+ return statusBuilder.setState(CTLogListUpdateState.LOG_LIST_INVALID).build();
}
}
- private boolean install(InputStream newContent, String version) throws IOException {
+ LogListUpdateStatus install(
+ InputStream newContent, String version, LogListUpdateStatus.Builder statusBuilder)
+ throws IOException {
// To support atomically replacing the old configuration directory with the new
// there's a bunch of steps. We create a new directory with the logs and then do
// an atomic update of the current symlink to point to the new directory.
@@ -100,7 +109,7 @@
if (newLogsDir.getCanonicalPath().equals(mCurrentLogsDirSymlink.getCanonicalPath())) {
Log.i(TAG, newLogsDir + " already exists, skipping install.");
deleteOldLogDirectories();
- return false;
+ return statusBuilder.setState(CTLogListUpdateState.VERSION_ALREADY_EXISTS).build();
}
// If the symlink has not been updated then the previous installation failed and
// this is a re-attempt. Clean-up leftover files and try again.
@@ -134,7 +143,7 @@
// 7. Cleanup
Log.i(TAG, "New logs installed at " + newLogsDir);
deleteOldLogDirectories();
- return true;
+ return statusBuilder.setState(CTLogListUpdateState.SUCCESS).build();
}
String getCompatVersion() {
diff --git a/networksecurity/service/src/com/android/server/net/ct/LogListUpdateStatus.java b/networksecurity/service/src/com/android/server/net/ct/LogListUpdateStatus.java
index 3d05857..3f9b762 100644
--- a/networksecurity/service/src/com/android/server/net/ct/LogListUpdateStatus.java
+++ b/networksecurity/service/src/com/android/server/net/ct/LogListUpdateStatus.java
@@ -19,6 +19,7 @@
import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SIGNATURE_INVALID;
import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SIGNATURE_NOT_FOUND;
import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SIGNATURE_VERIFICATION_FAILED;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SUCCESS;
import com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState;
@@ -52,6 +53,14 @@
return signature() != null && signature().length() > 0;
}
+ boolean isSuccessful() {
+ return state() == SUCCESS;
+ }
+
+ static LogListUpdateStatus getDefaultInstance() {
+ return builder().build();
+ }
+
@AutoValue.Builder
abstract static class Builder {
abstract Builder setState(CTLogListUpdateState updateState);
diff --git a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
index 08704d1..5443298 100644
--- a/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
+++ b/networksecurity/tests/unit/src/com/android/server/net/ct/CertificateTransparencyDownloaderTest.java
@@ -84,6 +84,7 @@
private CertificateTransparencyDownloader mCertificateTransparencyDownloader;
private long mNextDownloadId = 666;
+ private static final long LOG_LIST_TIMESTAMP = 123456789L;
@Before
public void setUp() throws IOException, GeneralSecurityException {
@@ -426,7 +427,7 @@
verify(mLogger, times(1))
.logCTLogListUpdateStateChangedEvent(mUpdateStatusCaptor.capture());
LogListUpdateStatus statusValue = mUpdateStatusCaptor.getValue();
- assertThat(statusValue.state()).isEqualTo(CTLogListUpdateState.VERSION_ALREADY_EXISTS);
+ assertThat(statusValue.state()).isEqualTo(CTLogListUpdateState.LOG_LIST_INVALID);
assertThat(statusValue.signature())
.isEqualTo(new String(signatureBytes, StandardCharsets.UTF_8));
}
@@ -466,7 +467,8 @@
}
@Test
- public void testDownloader_endToEndSuccess_installNewVersion() throws Exception {
+ public void testDownloader_endToEndSuccess_installNewVersion_andLogsSuccess() throws Exception {
+ // Arrange
String newVersion = "456";
File logListFile = makeLogListFile(newVersion);
File metadataFile = sign(logListFile);
@@ -474,6 +476,7 @@
assertNoVersionIsInstalled();
+ // Act
// 1. Start download of public key.
mCertificateTransparencyDownloader.startPublicKeyDownload();
@@ -491,7 +494,19 @@
mCertificateTransparencyDownloader.onReceive(
mContext, makeContentDownloadCompleteIntent(mCompatVersion, logListFile));
+ // Assert
assertInstallSuccessful(newVersion);
+ verify(mLogger, times(1))
+ .logCTLogListUpdateStateChangedEvent(mUpdateStatusCaptor.capture());
+
+ LogListUpdateStatus statusValue = mUpdateStatusCaptor.getValue();
+ assertThat(statusValue.state()).isEqualTo(CTLogListUpdateState.SUCCESS);
+ assertThat(statusValue.signature())
+ .isEqualTo(
+ new String(
+ Base64.getDecoder().decode(toByteArray(metadataFile)),
+ StandardCharsets.UTF_8));
+ assertThat(statusValue.logListTimestamp()).isEqualTo(LOG_LIST_TIMESTAMP);
}
private void assertNoVersionIsInstalled() {
@@ -600,7 +615,11 @@
File logListFile = File.createTempFile("log_list", "json");
try (OutputStream outputStream = new FileOutputStream(logListFile)) {
- outputStream.write(new JSONObject().put("version", version).toString().getBytes(UTF_8));
+ JSONObject contentJson =
+ new JSONObject()
+ .put("version", version)
+ .put("log_list_timestamp", LOG_LIST_TIMESTAMP);
+ outputStream.write(contentJson.toString().getBytes(UTF_8));
}
return logListFile;
diff --git a/networksecurity/tests/unit/src/com/android/server/net/ct/CompatibilityVersionTest.java b/networksecurity/tests/unit/src/com/android/server/net/ct/CompatibilityVersionTest.java
index 38fff48..2b8b3cd 100644
--- a/networksecurity/tests/unit/src/com/android/server/net/ct/CompatibilityVersionTest.java
+++ b/networksecurity/tests/unit/src/com/android/server/net/ct/CompatibilityVersionTest.java
@@ -15,6 +15,11 @@
*/
package com.android.server.net.ct;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.LOG_LIST_INVALID;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.SUCCESS;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.UNKNOWN_STATE;
+import static com.android.server.net.ct.CertificateTransparencyLogger.CTLogListUpdateState.VERSION_ALREADY_EXISTS;
+
import static com.google.common.truth.Truth.assertThat;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -37,6 +42,8 @@
public class CompatibilityVersionTest {
private static final String TEST_VERSION = "v123";
+ private static final long LOG_LIST_TIMESTAMP = 123456789L;
+ private static final String SIGNATURE = "fake_signature";
private final File mTestDir =
InstrumentationRegistry.getInstrumentation().getContext().getFilesDir();
@@ -52,6 +59,7 @@
@Test
public void testCompatibilityVersion_versionDirectory_setupSuccessful() {
File versionDir = mCompatVersion.getVersionDir();
+
assertThat(versionDir.exists()).isFalse();
assertThat(versionDir.getAbsolutePath()).startsWith(mTestDir.getAbsolutePath());
assertThat(versionDir.getAbsolutePath()).endsWith(TEST_VERSION);
@@ -60,6 +68,7 @@
@Test
public void testCompatibilityVersion_symlink_setupSuccessful() {
File dirSymlink = mCompatVersion.getLogsDirSymlink();
+
assertThat(dirSymlink.exists()).isFalse();
assertThat(dirSymlink.getAbsolutePath())
.startsWith(mCompatVersion.getVersionDir().getAbsolutePath());
@@ -68,18 +77,44 @@
@Test
public void testCompatibilityVersion_logsFile_setupSuccessful() {
File logsFile = mCompatVersion.getLogsFile();
+
assertThat(logsFile.exists()).isFalse();
assertThat(logsFile.getAbsolutePath())
.startsWith(mCompatVersion.getLogsDirSymlink().getAbsolutePath());
}
@Test
+ public void testCompatibilityVersion_installSuccessful_keepsStatusDetails() throws Exception {
+ String version = "i_am_version";
+ JSONObject logList = makeLogList(version, "i_am_content");
+
+ try (InputStream inputStream = asStream(logList)) {
+ assertThat(
+ mCompatVersion.install(
+ inputStream,
+ LogListUpdateStatus.builder()
+ .setSignature(SIGNATURE)
+ .setState(UNKNOWN_STATE)))
+ .isEqualTo(
+ LogListUpdateStatus.builder()
+ .setSignature(SIGNATURE)
+ .setLogListTimestamp(LOG_LIST_TIMESTAMP)
+ // Ensure the state is correctly overridden to SUCCESS
+ .setState(SUCCESS)
+ .build());
+ }
+ }
+
+ @Test
public void testCompatibilityVersion_installSuccessful() throws Exception {
String version = "i_am_version";
JSONObject logList = makeLogList(version, "i_am_content");
try (InputStream inputStream = asStream(logList)) {
- assertThat(mCompatVersion.install(inputStream)).isTrue();
+ assertThat(
+ mCompatVersion.install(
+ inputStream, LogListUpdateStatus.builder()))
+ .isEqualTo(getSuccessfulUpdateStatus());
}
File logListFile = mCompatVersion.getLogsFile();
@@ -107,7 +142,10 @@
@Test
public void testCompatibilityVersion_deleteSuccessfully() throws Exception {
try (InputStream inputStream = asStream(makeLogList(/* version= */ "123"))) {
- assertThat(mCompatVersion.install(inputStream)).isTrue();
+ assertThat(
+ mCompatVersion.install(
+ inputStream, LogListUpdateStatus.builder()))
+ .isEqualTo(getSuccessfulUpdateStatus());
}
mCompatVersion.delete();
@@ -118,7 +156,10 @@
@Test
public void testCompatibilityVersion_invalidLogList() throws Exception {
try (InputStream inputStream = new ByteArrayInputStream(("not_a_valid_list".getBytes()))) {
- assertThat(mCompatVersion.install(inputStream)).isFalse();
+ assertThat(
+ mCompatVersion.install(
+ inputStream, LogListUpdateStatus.builder()))
+ .isEqualTo(LogListUpdateStatus.builder().setState(LOG_LIST_INVALID).build());
}
assertThat(mCompatVersion.getLogsFile().exists()).isFalse();
@@ -138,7 +179,10 @@
JSONObject newLogList = makeLogList(existingVersion, "i_am_the_real_content");
try (InputStream inputStream = asStream(newLogList)) {
- assertThat(mCompatVersion.install(inputStream)).isTrue();
+ assertThat(
+ mCompatVersion.install(
+ inputStream, LogListUpdateStatus.builder()))
+ .isEqualTo(getSuccessfulUpdateStatus());
}
assertThat(readAsString(logsListFile)).isEqualTo(newLogList.toString());
@@ -149,11 +193,21 @@
String existingVersion = "666";
JSONObject existingLogList = makeLogList(existingVersion, "i_was_installed_successfully");
try (InputStream inputStream = asStream(existingLogList)) {
- assertThat(mCompatVersion.install(inputStream)).isTrue();
+ assertThat(
+ mCompatVersion.install(
+ inputStream, LogListUpdateStatus.builder()))
+ .isEqualTo(getSuccessfulUpdateStatus());
}
try (InputStream inputStream = asStream(makeLogList(existingVersion, "i_am_ignored"))) {
- assertThat(mCompatVersion.install(inputStream)).isFalse();
+ assertThat(
+ mCompatVersion.install(
+ inputStream, LogListUpdateStatus.builder()))
+ .isEqualTo(
+ LogListUpdateStatus.builder()
+ .setState(VERSION_ALREADY_EXISTS)
+ .setLogListTimestamp(LOG_LIST_TIMESTAMP)
+ .build());
}
assertThat(readAsString(mCompatVersion.getLogsFile()))
@@ -165,13 +219,22 @@
}
private static JSONObject makeLogList(String version) throws JSONException {
- return new JSONObject().put("version", version);
+ return new JSONObject()
+ .put("version", version)
+ .put("log_list_timestamp", LOG_LIST_TIMESTAMP);
}
private static JSONObject makeLogList(String version, String content) throws JSONException {
return makeLogList(version).put("content", content);
}
+ private static LogListUpdateStatus getSuccessfulUpdateStatus() {
+ return LogListUpdateStatus.builder()
+ .setState(SUCCESS)
+ .setLogListTimestamp(LOG_LIST_TIMESTAMP)
+ .build();
+ }
+
private static String readAsString(File file) throws IOException {
try (InputStream in = new FileInputStream(file)) {
return new String(in.readAllBytes());