Add a consecutive failure count to the CT DataStore
We plan on only logging failure metrics after 10
consecutive failures, so we need some way of storing it.
Fixes: 384473786
Test: atest NetworkSecurityUnitTests
Change-Id: Ib316309864c536893d08be2dc59b8a70965ebde6
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 56a5ee5..ef0d0ea 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyDownloader.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.server.net.ct;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -201,9 +202,8 @@
String version = null;
try (InputStream inputStream = mContext.getContentResolver().openInputStream(contentUri)) {
- version =
- new JSONObject(new String(inputStream.readAllBytes(), UTF_8))
- .getString("version");
+ version = new JSONObject(new String(inputStream.readAllBytes(), UTF_8))
+ .getString("version");
} catch (JSONException | IOException e) {
Log.e(TAG, "Could not extract version from log list", e);
return;
@@ -219,13 +219,44 @@
if (success) {
// Update information about the stored version on successful install.
mDataStore.setProperty(Config.VERSION, version);
+
+ // Reset the number of consecutive log list failure updates back to zero.
+ mDataStore.setPropertyLong(Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* value= */ 0L);
mDataStore.store();
+ } else {
+ if (updateFailureCount()) {
+ // TODO(378626065): Report FAILURE_VERSION_ALREADY_EXISTS failure via statsd.
+ }
}
}
private void handleDownloadFailed(DownloadStatus status) {
Log.e(TAG, "Download failed with " + status);
- // TODO(378626065): Report failure via statsd.
+
+ if (updateFailureCount()) {
+ // TODO(378626065): Report download failure via statsd.
+ }
+ }
+
+ /**
+ * Updates the data store with the current number of consecutive log list update failures.
+ *
+ * @return whether the failure count exceeds the threshold and should be logged.
+ */
+ private boolean updateFailureCount() {
+ long failure_count = mDataStore.getPropertyLong(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0L);
+ long new_failure_count = failure_count + 1L;
+
+ mDataStore.setPropertyLong(Config.LOG_LIST_UPDATE_FAILURE_COUNT, new_failure_count);
+ mDataStore.store();
+
+ boolean shouldReport = new_failure_count >= Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD;
+ if (shouldReport) {
+ Log.e(TAG,
+ "Log list update failure count exceeds threshold: " + new_failure_count);
+ }
+ return shouldReport;
}
private long download(String url) {
diff --git a/networksecurity/service/src/com/android/server/net/ct/Config.java b/networksecurity/service/src/com/android/server/net/ct/Config.java
index 70d8e42..edd6a80 100644
--- a/networksecurity/service/src/com/android/server/net/ct/Config.java
+++ b/networksecurity/service/src/com/android/server/net/ct/Config.java
@@ -54,10 +54,14 @@
static final String METADATA_DOWNLOAD_ID = "metadata_download_id";
static final String PUBLIC_KEY_URL = "public_key_url";
static final String PUBLIC_KEY_DOWNLOAD_ID = "public_key_download_id";
+ static final String LOG_LIST_UPDATE_FAILURE_COUNT = "log_list_update_failure_count";
// URLs
static final String URL_PREFIX = "https://www.gstatic.com/android/certificate_transparency/";
static final String URL_LOG_LIST = URL_PREFIX + "log_list.json";
static final String URL_SIGNATURE = URL_PREFIX + "log_list.sig";
static final String URL_PUBLIC_KEY = URL_PREFIX + "log_list.pub";
+
+ // Threshold amounts
+ static final long LOG_LIST_UPDATE_FAILURE_THRESHOLD = 10L;
}
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 ffa1283..5c4a4e5 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
@@ -186,6 +186,47 @@
}
@Test
+ public void testDownloader_publicKeyDownloadFail_failureThresholdExceeded_logsFailure()
+ throws Exception {
+ long publicKeyId = mCertificateTransparencyDownloader.startPublicKeyDownload();
+ // Set the failure count to just below the threshold
+ mDataStore.setPropertyLong(Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ setFailedDownload(
+ publicKeyId, // Failure cases where we give up on the download.
+ DownloadManager.ERROR_INSUFFICIENT_SPACE,
+ DownloadManager.ERROR_HTTP_DATA_ERROR);
+ Intent downloadCompleteIntent = makeDownloadCompleteIntent(publicKeyId);
+
+ mCertificateTransparencyDownloader.onReceive(mContext, downloadCompleteIntent);
+
+ assertThat(mDataStore.getPropertyLong(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0L))
+ .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ // TODO(378626065): Verify logged failure via statsd.
+ }
+
+ @Test
+ public void testDownloader_publicKeyDownloadFail_failureThresholdNotMet_doesNotLog()
+ throws Exception {
+ long publicKeyId = mCertificateTransparencyDownloader.startPublicKeyDownload();
+ // Set the failure count to just below the threshold
+ mDataStore.setPropertyLong(Config.LOG_LIST_UPDATE_FAILURE_COUNT, 0);
+ setFailedDownload(
+ publicKeyId, // Failure cases where we give up on the download.
+ DownloadManager.ERROR_INSUFFICIENT_SPACE,
+ DownloadManager.ERROR_HTTP_DATA_ERROR);
+ Intent downloadCompleteIntent = makeDownloadCompleteIntent(publicKeyId);
+
+ mCertificateTransparencyDownloader.onReceive(mContext, downloadCompleteIntent);
+
+ assertThat(mDataStore.getPropertyLong(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0L))
+ .isEqualTo(1);
+ // TODO(378626065): Verify no failure logged via statsd.
+ }
+
+ @Test
public void testDownloader_metadataDownloadSuccess_startContentDownload() {
long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
setSuccessfulDownload(metadataId, new File("log_list.sig"));
@@ -215,6 +256,49 @@
}
@Test
+ public void testDownloader_metadataDownloadFail_failureThresholdExceeded_logsFailure()
+ throws Exception {
+ long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
+ // Set the failure count to just below the threshold
+ mDataStore.setPropertyLong(Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ setFailedDownload(
+ metadataId,
+ // Failure cases where we give up on the download.
+ DownloadManager.ERROR_INSUFFICIENT_SPACE,
+ DownloadManager.ERROR_HTTP_DATA_ERROR);
+ Intent downloadCompleteIntent = makeDownloadCompleteIntent(metadataId);
+
+ mCertificateTransparencyDownloader.onReceive(mContext, downloadCompleteIntent);
+
+ assertThat(mDataStore.getPropertyLong(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0L))
+ .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ // TODO(378626065): Verify logged failure via statsd.
+ }
+
+ @Test
+ public void testDownloader_metadataDownloadFail_failureThresholdNotMet_doesNotLog()
+ throws Exception {
+ long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
+ // Set the failure count to just below the threshold
+ mDataStore.setPropertyLong(Config.LOG_LIST_UPDATE_FAILURE_COUNT, 0);
+ setFailedDownload(
+ metadataId,
+ // Failure cases where we give up on the download.
+ DownloadManager.ERROR_INSUFFICIENT_SPACE,
+ DownloadManager.ERROR_HTTP_DATA_ERROR);
+ Intent downloadCompleteIntent = makeDownloadCompleteIntent(metadataId);
+
+ mCertificateTransparencyDownloader.onReceive(mContext, downloadCompleteIntent);
+
+ assertThat(mDataStore.getPropertyLong(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0L))
+ .isEqualTo(1);
+ // TODO(378626065): Verify no failure logged via statsd.
+ }
+
+ @Test
public void testDownloader_contentDownloadSuccess_installSuccess_updateDataStore()
throws Exception {
String newVersion = "456";
@@ -254,6 +338,49 @@
}
@Test
+ public void testDownloader_contentDownloadFail_failureThresholdExceeded_logsFailure()
+ throws Exception {
+ long contentId = mCertificateTransparencyDownloader.startContentDownload();
+ // Set the failure count to just below the threshold
+ mDataStore.setPropertyLong(Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ setFailedDownload(
+ contentId,
+ // Failure cases where we give up on the download.
+ DownloadManager.ERROR_INSUFFICIENT_SPACE,
+ DownloadManager.ERROR_HTTP_DATA_ERROR);
+ Intent downloadCompleteIntent = makeDownloadCompleteIntent(contentId);
+
+ mCertificateTransparencyDownloader.onReceive(mContext, downloadCompleteIntent);
+
+ assertThat(mDataStore.getPropertyLong(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0L))
+ .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ // TODO(378626065): Verify logged failure via statsd.
+ }
+
+ @Test
+ public void testDownloader_contentDownloadFail_failureThresholdNotMet_doesNotLog()
+ throws Exception {
+ long contentId = mCertificateTransparencyDownloader.startContentDownload();
+ // Set the failure count to just below the threshold
+ mDataStore.setPropertyLong(Config.LOG_LIST_UPDATE_FAILURE_COUNT, 0);
+ setFailedDownload(
+ contentId,
+ // Failure cases where we give up on the download.
+ DownloadManager.ERROR_INSUFFICIENT_SPACE,
+ DownloadManager.ERROR_HTTP_DATA_ERROR);
+ Intent downloadCompleteIntent = makeDownloadCompleteIntent(contentId);
+
+ mCertificateTransparencyDownloader.onReceive(mContext, downloadCompleteIntent);
+
+ assertThat(mDataStore.getPropertyLong(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0L))
+ .isEqualTo(1);
+ // TODO(378626065): Verify no failure logged via statsd.
+ }
+
+ @Test
public void testDownloader_contentDownloadSuccess_installFail_doNotUpdateDataStore()
throws Exception {
File logListFile = makeLogListFile("456");
@@ -275,6 +402,59 @@
}
@Test
+ public void
+ testDownloader_contentDownloadSuccess_installFail_failureThresholdExceeded_logsFailure()
+ throws Exception {
+ File logListFile = makeLogListFile("456");
+ File metadataFile = sign(logListFile);
+ mSignatureVerifier.setPublicKey(mPublicKey);
+ long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
+ setSuccessfulDownload(metadataId, metadataFile);
+ long contentId = mCertificateTransparencyDownloader.startContentDownload();
+ setSuccessfulDownload(contentId, logListFile);
+ // Set the failure count to just below the threshold
+ mDataStore.setPropertyLong(Config.LOG_LIST_UPDATE_FAILURE_COUNT,
+ Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD - 1);
+ when(mCertificateTransparencyInstaller.install(
+ eq(Config.COMPATIBILITY_VERSION), any(), anyString()))
+ .thenReturn(false);
+
+ mCertificateTransparencyDownloader.onReceive(
+ mContext, makeDownloadCompleteIntent(contentId));
+
+ assertThat(mDataStore.getPropertyLong(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0L))
+ .isEqualTo(Config.LOG_LIST_UPDATE_FAILURE_THRESHOLD);
+ // TODO(378626065): Verify logged failure via statsd.
+ }
+
+ @Test
+ public void
+ testDownloader_contentDownloadSuccess_installFail_failureThresholdNotMet_doesNotLog()
+ throws Exception {
+ File logListFile = makeLogListFile("456");
+ File metadataFile = sign(logListFile);
+ mSignatureVerifier.setPublicKey(mPublicKey);
+ long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
+ setSuccessfulDownload(metadataId, metadataFile);
+ long contentId = mCertificateTransparencyDownloader.startContentDownload();
+ setSuccessfulDownload(contentId, logListFile);
+ // Set the failure count to just below the threshold
+ mDataStore.setPropertyLong(Config.LOG_LIST_UPDATE_FAILURE_COUNT, 0);
+ when(mCertificateTransparencyInstaller.install(
+ eq(Config.COMPATIBILITY_VERSION), any(), anyString()))
+ .thenReturn(false);
+
+ mCertificateTransparencyDownloader.onReceive(
+ mContext, makeDownloadCompleteIntent(contentId));
+
+ assertThat(mDataStore.getPropertyLong(
+ Config.LOG_LIST_UPDATE_FAILURE_COUNT, /* defaultValue= */ 0L))
+ .isEqualTo(1);
+ // TODO(378626065): Verify no failure logged via statsd.
+ }
+
+ @Test
public void testDownloader_contentDownloadSuccess_verificationFail_doNotInstall()
throws Exception {
File logListFile = makeLogListFile("456");