Simplify the Android CT properties

Since we are moving away from runtime flags, the CT URLs for the log
list, signature, and public key will be hardcoded. I took the occasion
to simplify the logic for handling the Android CT properties and removed
the various "_PENDING" properties. They were making everything much
harder to follow.

Flag: com.android.net.ct.flags.certificate_transparency_job
Bug: 319829948
Test: NetworkSecurityUnitTests
Change-Id: I74bb946eccb5f376ee161ace2f151bf6ea1bdcff
This commit is contained in:
Sandro Montanari 2024-11-27 13:58:20 +00:00
commit 9190aed0f8
5 changed files with 190 additions and 221 deletions

View file

@ -75,34 +75,31 @@ class CertificateTransparencyDownloader extends BroadcastReceiver {
}
}
void startPublicKeyDownload(String publicKeyUrl) {
long downloadId = download(publicKeyUrl);
if (downloadId == -1) {
Log.e(TAG, "Metadata download request failed for " + publicKeyUrl);
return;
long startPublicKeyDownload() {
long downloadId = download(mDataStore.getProperty(Config.PUBLIC_KEY_URL));
if (downloadId != -1) {
mDataStore.setPropertyLong(Config.PUBLIC_KEY_DOWNLOAD_ID, downloadId);
mDataStore.store();
}
mDataStore.setPropertyLong(Config.PUBLIC_KEY_URL_KEY, downloadId);
mDataStore.store();
return downloadId;
}
void startMetadataDownload(String metadataUrl) {
long downloadId = download(metadataUrl);
if (downloadId == -1) {
Log.e(TAG, "Metadata download request failed for " + metadataUrl);
return;
long startMetadataDownload() {
long downloadId = download(mDataStore.getProperty(Config.METADATA_URL));
if (downloadId != -1) {
mDataStore.setPropertyLong(Config.METADATA_DOWNLOAD_ID, downloadId);
mDataStore.store();
}
mDataStore.setPropertyLong(Config.METADATA_URL_KEY, downloadId);
mDataStore.store();
return downloadId;
}
void startContentDownload(String contentUrl) {
long downloadId = download(contentUrl);
if (downloadId == -1) {
Log.e(TAG, "Content download request failed for " + contentUrl);
return;
long startContentDownload() {
long downloadId = download(mDataStore.getProperty(Config.CONTENT_URL));
if (downloadId != -1) {
mDataStore.setPropertyLong(Config.CONTENT_DOWNLOAD_ID, downloadId);
mDataStore.store();
}
mDataStore.setPropertyLong(Config.CONTENT_URL_KEY, downloadId);
mDataStore.store();
return downloadId;
}
@Override
@ -157,7 +154,11 @@ class CertificateTransparencyDownloader extends BroadcastReceiver {
return;
}
startMetadataDownload(mDataStore.getProperty(Config.METADATA_URL_PENDING));
if (startMetadataDownload() == -1) {
Log.e(TAG, "Metadata download not started.");
} else if (Config.DEBUG) {
Log.d(TAG, "Metadata download started successfully.");
}
}
private void handleMetadataDownloadCompleted(long downloadId) {
@ -166,7 +167,11 @@ class CertificateTransparencyDownloader extends BroadcastReceiver {
handleDownloadFailed(status);
return;
}
startContentDownload(mDataStore.getProperty(Config.CONTENT_URL_PENDING));
if (startContentDownload() == -1) {
Log.e(TAG, "Content download not started.");
} else if (Config.DEBUG) {
Log.d(TAG, "Content download started successfully.");
}
}
private void handleContentDownloadCompleted(long downloadId) {
@ -204,8 +209,6 @@ class CertificateTransparencyDownloader extends BroadcastReceiver {
return;
}
String contentUrl = mDataStore.getProperty(Config.CONTENT_URL_PENDING);
String metadataUrl = mDataStore.getProperty(Config.METADATA_URL_PENDING);
try (InputStream inputStream = mContext.getContentResolver().openInputStream(contentUri)) {
success = mInstaller.install(Config.COMPATIBILITY_VERSION, inputStream, version);
} catch (IOException e) {
@ -216,8 +219,6 @@ class CertificateTransparencyDownloader extends BroadcastReceiver {
if (success) {
// Update information about the stored version on successful install.
mDataStore.setProperty(Config.VERSION, version);
mDataStore.setProperty(Config.CONTENT_URL, contentUrl);
mDataStore.setProperty(Config.METADATA_URL, metadataUrl);
mDataStore.store();
}
}
@ -236,30 +237,60 @@ class CertificateTransparencyDownloader extends BroadcastReceiver {
}
}
@VisibleForTesting
long getPublicKeyDownloadId() {
return mDataStore.getPropertyLong(Config.PUBLIC_KEY_DOWNLOAD_ID, -1);
}
@VisibleForTesting
long getMetadataDownloadId() {
return mDataStore.getPropertyLong(Config.METADATA_DOWNLOAD_ID, -1);
}
@VisibleForTesting
long getContentDownloadId() {
return mDataStore.getPropertyLong(Config.CONTENT_DOWNLOAD_ID, -1);
}
@VisibleForTesting
boolean hasPublicKeyDownloadId() {
return getPublicKeyDownloadId() != -1;
}
@VisibleForTesting
boolean hasMetadataDownloadId() {
return getMetadataDownloadId() != -1;
}
@VisibleForTesting
boolean hasContentDownloadId() {
return getContentDownloadId() != -1;
}
@VisibleForTesting
boolean isPublicKeyDownloadId(long downloadId) {
return mDataStore.getPropertyLong(Config.PUBLIC_KEY_URL_KEY, -1) == downloadId;
return getPublicKeyDownloadId() == downloadId;
}
@VisibleForTesting
boolean isMetadataDownloadId(long downloadId) {
return mDataStore.getPropertyLong(Config.METADATA_URL_KEY, -1) == downloadId;
return getMetadataDownloadId() == downloadId;
}
@VisibleForTesting
boolean isContentDownloadId(long downloadId) {
return mDataStore.getPropertyLong(Config.CONTENT_URL_KEY, -1) == downloadId;
return getContentDownloadId() == downloadId;
}
private Uri getPublicKeyDownloadUri() {
return mDownloadHelper.getUri(mDataStore.getPropertyLong(Config.PUBLIC_KEY_URL_KEY, -1));
return mDownloadHelper.getUri(getPublicKeyDownloadId());
}
private Uri getMetadataDownloadUri() {
return mDownloadHelper.getUri(mDataStore.getPropertyLong(Config.METADATA_URL_KEY, -1));
return mDownloadHelper.getUri(getMetadataDownloadId());
}
private Uri getContentDownloadUri() {
return mDownloadHelper.getUri(mDataStore.getPropertyLong(Config.CONTENT_URL_KEY, -1));
return mDownloadHelper.getUri(getContentDownloadId());
}
}

View file

@ -115,11 +115,14 @@ class CertificateTransparencyFlagsListener implements DeviceConfig.OnPropertiesC
// TODO: handle the case where there is already a pending download.
mDataStore.setProperty(Config.VERSION_PENDING, newVersion);
mDataStore.setProperty(Config.CONTENT_URL_PENDING, newContentUrl);
mDataStore.setProperty(Config.METADATA_URL_PENDING, newMetadataUrl);
mDataStore.setProperty(Config.CONTENT_URL, newContentUrl);
mDataStore.setProperty(Config.METADATA_URL, newMetadataUrl);
mDataStore.store();
mCertificateTransparencyDownloader.startMetadataDownload(newMetadataUrl);
if (mCertificateTransparencyDownloader.startMetadataDownload() == -1) {
Log.e(TAG, "Metadata download not started.");
} else if (Config.DEBUG) {
Log.d(TAG, "Metadata download started successfully.");
}
}
}

View file

@ -78,11 +78,15 @@ public class CertificateTransparencyJob extends BroadcastReceiver {
Log.d(TAG, "Starting CT daily job.");
}
mDataStore.setProperty(Config.CONTENT_URL_PENDING, Config.URL_LOG_LIST);
mDataStore.setProperty(Config.METADATA_URL_PENDING, Config.URL_SIGNATURE);
mDataStore.setProperty(Config.PUBLIC_KEY_URL_PENDING, Config.URL_PUBLIC_KEY);
mDataStore.setProperty(Config.CONTENT_URL, Config.URL_LOG_LIST);
mDataStore.setProperty(Config.METADATA_URL, Config.URL_SIGNATURE);
mDataStore.setProperty(Config.PUBLIC_KEY_URL, Config.URL_PUBLIC_KEY);
mDataStore.store();
mCertificateTransparencyDownloader.startPublicKeyDownload(Config.URL_PUBLIC_KEY);
if (mCertificateTransparencyDownloader.startPublicKeyDownload() == -1) {
Log.e(TAG, "Public key download not started.");
} else if (Config.DEBUG) {
Log.d(TAG, "Public key download started successfully.");
}
}
}

View file

@ -47,21 +47,17 @@ final class Config {
static final String FLAG_PUBLIC_KEY = FLAGS_PREFIX + "public_key";
// properties
static final String VERSION_PENDING = "version_pending";
static final String VERSION = "version";
static final String CONTENT_URL_PENDING = "content_url_pending";
static final String CONTENT_URL = "content_url";
static final String CONTENT_URL_KEY = "content_url_key";
static final String METADATA_URL_PENDING = "metadata_url_pending";
static final String CONTENT_DOWNLOAD_ID = "content_download_id";
static final String METADATA_URL = "metadata_url";
static final String METADATA_URL_KEY = "metadata_url_key";
static final String PUBLIC_KEY_URL_PENDING = "public_key_url_pending";
static final String METADATA_DOWNLOAD_ID = "metadata_download_id";
static final String PUBLIC_KEY_URL = "public_key_url";
static final String PUBLIC_KEY_URL_KEY = "public_key_url_key";
static final String PUBLIC_KEY_DOWNLOAD_ID = "public_key_download_id";
// URLs
static final String URL_BASE = "https://www.gstatic.com/android/certificate_transparency/";
static final String URL_LOG_LIST = URL_BASE + "log_list.json";
static final String URL_SIGNATURE = URL_BASE + "log_list.sig";
static final String URL_PUBLIC_KEY = URL_BASE + "log_list.pub";
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";
}

View file

@ -27,14 +27,16 @@ import static org.mockito.Mockito.when;
import static java.nio.charset.StandardCharsets.UTF_8;
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.net.ct.DownloadHelper.DownloadStatus;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.After;
@ -63,7 +65,7 @@ import java.util.Base64;
@RunWith(JUnit4.class)
public class CertificateTransparencyDownloaderTest {
@Mock private DownloadHelper mDownloadHelper;
@Mock private DownloadManager mDownloadManager;
@Mock private CertificateTransparencyInstaller mCertificateTransparencyInstaller;
private PrivateKey mPrivateKey;
@ -79,7 +81,6 @@ public class CertificateTransparencyDownloaderTest {
@Before
public void setUp() throws IOException, GeneralSecurityException {
MockitoAnnotations.initMocks(this);
KeyPairGenerator instance = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair = instance.generateKeyPair();
mPrivateKey = keyPair.getPrivate();
@ -88,16 +89,17 @@ public class CertificateTransparencyDownloaderTest {
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mTempFile = File.createTempFile("datastore-test", ".properties");
mDataStore = new DataStore(mTempFile);
mDataStore.load();
mSignatureVerifier = new SignatureVerifier(mContext);
mCertificateTransparencyDownloader =
new CertificateTransparencyDownloader(
mContext,
mDataStore,
mDownloadHelper,
new DownloadHelper(mDownloadManager),
mSignatureVerifier,
mCertificateTransparencyInstaller);
prepareDataStore();
prepareDownloadManager();
}
@After
@ -108,116 +110,108 @@ public class CertificateTransparencyDownloaderTest {
@Test
public void testDownloader_startPublicKeyDownload() {
String publicKeyUrl = "http://test-public-key.org";
long downloadId = preparePublicKeyDownload(publicKeyUrl);
assertThat(mCertificateTransparencyDownloader.hasPublicKeyDownloadId()).isFalse();
long downloadId = mCertificateTransparencyDownloader.startPublicKeyDownload();
assertThat(mCertificateTransparencyDownloader.isPublicKeyDownloadId(downloadId)).isFalse();
mCertificateTransparencyDownloader.startPublicKeyDownload(publicKeyUrl);
assertThat(mCertificateTransparencyDownloader.hasPublicKeyDownloadId()).isTrue();
assertThat(mCertificateTransparencyDownloader.isPublicKeyDownloadId(downloadId)).isTrue();
}
@Test
public void testDownloader_startMetadataDownload() {
String metadataUrl = "http://test-metadata.org";
long downloadId = prepareMetadataDownload(metadataUrl);
assertThat(mCertificateTransparencyDownloader.hasMetadataDownloadId()).isFalse();
long downloadId = mCertificateTransparencyDownloader.startMetadataDownload();
assertThat(mCertificateTransparencyDownloader.isMetadataDownloadId(downloadId)).isFalse();
mCertificateTransparencyDownloader.startMetadataDownload(metadataUrl);
assertThat(mCertificateTransparencyDownloader.hasMetadataDownloadId()).isTrue();
assertThat(mCertificateTransparencyDownloader.isMetadataDownloadId(downloadId)).isTrue();
}
@Test
public void testDownloader_startContentDownload() {
String contentUrl = "http://test-content.org";
long downloadId = prepareContentDownload(contentUrl);
assertThat(mCertificateTransparencyDownloader.hasContentDownloadId()).isFalse();
long downloadId = mCertificateTransparencyDownloader.startContentDownload();
assertThat(mCertificateTransparencyDownloader.isContentDownloadId(downloadId)).isFalse();
mCertificateTransparencyDownloader.startContentDownload(contentUrl);
assertThat(mCertificateTransparencyDownloader.hasContentDownloadId()).isTrue();
assertThat(mCertificateTransparencyDownloader.isContentDownloadId(downloadId)).isTrue();
}
@Test
public void testDownloader_publicKeyDownloadSuccess_updatePublicKey_startMetadataDownload()
throws Exception {
long publicKeyId = prepareSuccessfulPublicKeyDownload(writePublicKeyToFile(mPublicKey));
long metadataId = prepareMetadataDownload("http://test-metadata.org");
long publicKeyId = mCertificateTransparencyDownloader.startPublicKeyDownload();
setSuccessfulDownload(publicKeyId, writePublicKeyToFile(mPublicKey));
assertThat(mSignatureVerifier.getPublicKey()).isEmpty();
assertThat(mCertificateTransparencyDownloader.isMetadataDownloadId(metadataId)).isFalse();
assertThat(mCertificateTransparencyDownloader.hasMetadataDownloadId()).isFalse();
mCertificateTransparencyDownloader.onReceive(
mContext, makeDownloadCompleteIntent(publicKeyId));
assertThat(mSignatureVerifier.getPublicKey()).hasValue(mPublicKey);
assertThat(mCertificateTransparencyDownloader.isMetadataDownloadId(metadataId)).isTrue();
assertThat(mCertificateTransparencyDownloader.hasMetadataDownloadId()).isTrue();
}
@Test
public void
testDownloader_publicKeyDownloadSuccess_updatePublicKeyFail_doNotStartMetadataDownload()
throws Exception {
long publicKeyId =
prepareSuccessfulPublicKeyDownload(
writeToFile("i_am_not_a_base64_encoded_public_key".getBytes()));
long metadataId = prepareMetadataDownload("http://test-metadata.org");
long publicKeyId = mCertificateTransparencyDownloader.startPublicKeyDownload();
setSuccessfulDownload(
publicKeyId, writeToFile("i_am_not_a_base64_encoded_public_key".getBytes()));
assertThat(mSignatureVerifier.getPublicKey()).isEmpty();
assertThat(mCertificateTransparencyDownloader.isMetadataDownloadId(metadataId)).isFalse();
assertThat(mCertificateTransparencyDownloader.hasMetadataDownloadId()).isFalse();
mCertificateTransparencyDownloader.onReceive(
mContext, makeDownloadCompleteIntent(publicKeyId));
assertThat(mSignatureVerifier.getPublicKey()).isEmpty();
assertThat(mCertificateTransparencyDownloader.isMetadataDownloadId(metadataId)).isFalse();
verify(mDownloadHelper, never()).startDownload(anyString());
assertThat(mCertificateTransparencyDownloader.hasMetadataDownloadId()).isFalse();
}
@Test
public void testDownloader_publicKeyDownloadFail_doNotUpdatePublicKey() throws Exception {
long publicKeyId =
prepareFailedPublicKeyDownload(
// Failure cases where we give up on the download.
DownloadManager.ERROR_INSUFFICIENT_SPACE,
DownloadManager.ERROR_HTTP_DATA_ERROR);
long publicKeyId = mCertificateTransparencyDownloader.startPublicKeyDownload();
setFailedDownload(
publicKeyId, // Failure cases where we give up on the download.
DownloadManager.ERROR_INSUFFICIENT_SPACE,
DownloadManager.ERROR_HTTP_DATA_ERROR);
Intent downloadCompleteIntent = makeDownloadCompleteIntent(publicKeyId);
long metadataId = prepareMetadataDownload("http://test-metadata.org");
assertThat(mSignatureVerifier.getPublicKey()).isEmpty();
assertThat(mCertificateTransparencyDownloader.isMetadataDownloadId(metadataId)).isFalse();
assertThat(mCertificateTransparencyDownloader.hasMetadataDownloadId()).isFalse();
mCertificateTransparencyDownloader.onReceive(mContext, downloadCompleteIntent);
mCertificateTransparencyDownloader.onReceive(mContext, downloadCompleteIntent);
assertThat(mSignatureVerifier.getPublicKey()).isEmpty();
assertThat(mCertificateTransparencyDownloader.isMetadataDownloadId(metadataId)).isFalse();
verify(mDownloadHelper, never()).startDownload(anyString());
assertThat(mCertificateTransparencyDownloader.hasMetadataDownloadId()).isFalse();
}
@Test
public void testDownloader_metadataDownloadSuccess_startContentDownload() {
long metadataId = prepareSuccessfulMetadataDownload(new File("log_list.sig"));
long contentId = prepareContentDownload("http://test-content.org");
long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
setSuccessfulDownload(metadataId, new File("log_list.sig"));
assertThat(mCertificateTransparencyDownloader.isContentDownloadId(contentId)).isFalse();
assertThat(mCertificateTransparencyDownloader.hasContentDownloadId()).isFalse();
mCertificateTransparencyDownloader.onReceive(
mContext, makeDownloadCompleteIntent(metadataId));
assertThat(mCertificateTransparencyDownloader.isContentDownloadId(contentId)).isTrue();
assertThat(mCertificateTransparencyDownloader.hasContentDownloadId()).isTrue();
}
@Test
public void testDownloader_metadataDownloadFail_doNotStartContentDownload() {
long metadataId =
prepareFailedMetadataDownload(
// Failure cases where we give up on the download.
DownloadManager.ERROR_INSUFFICIENT_SPACE,
DownloadManager.ERROR_HTTP_DATA_ERROR);
long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
setFailedDownload(
metadataId,
// Failure cases where we give up on the download.
DownloadManager.ERROR_INSUFFICIENT_SPACE,
DownloadManager.ERROR_HTTP_DATA_ERROR);
Intent downloadCompleteIntent = makeDownloadCompleteIntent(metadataId);
long contentId = prepareContentDownload("http://test-content.org");
assertThat(mCertificateTransparencyDownloader.isContentDownloadId(contentId)).isFalse();
assertThat(mCertificateTransparencyDownloader.hasContentDownloadId()).isFalse();
mCertificateTransparencyDownloader.onReceive(mContext, downloadCompleteIntent);
mCertificateTransparencyDownloader.onReceive(mContext, downloadCompleteIntent);
assertThat(mCertificateTransparencyDownloader.isContentDownloadId(contentId)).isFalse();
verify(mDownloadHelper, never()).startDownload(anyString());
assertThat(mCertificateTransparencyDownloader.hasContentDownloadId()).isFalse();
}
@Test
@ -227,8 +221,10 @@ public class CertificateTransparencyDownloaderTest {
File logListFile = makeLogListFile(newVersion);
File metadataFile = sign(logListFile);
mSignatureVerifier.setPublicKey(mPublicKey);
prepareSuccessfulMetadataDownload(metadataFile);
long contentId = prepareSuccessfulContentDownload(logListFile);
long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
setSuccessfulDownload(metadataId, metadataFile);
long contentId = mCertificateTransparencyDownloader.startContentDownload();
setSuccessfulDownload(contentId, logListFile);
when(mCertificateTransparencyInstaller.install(
eq(Config.COMPATIBILITY_VERSION), any(), anyString()))
.thenReturn(true);
@ -242,11 +238,12 @@ public class CertificateTransparencyDownloaderTest {
@Test
public void testDownloader_contentDownloadFail_doNotInstall() throws Exception {
long contentId =
prepareFailedContentDownload(
// Failure cases where we give up on the download.
DownloadManager.ERROR_INSUFFICIENT_SPACE,
DownloadManager.ERROR_HTTP_DATA_ERROR);
long contentId = mCertificateTransparencyDownloader.startContentDownload();
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);
@ -262,8 +259,10 @@ public class CertificateTransparencyDownloaderTest {
File logListFile = makeLogListFile("456");
File metadataFile = sign(logListFile);
mSignatureVerifier.setPublicKey(mPublicKey);
prepareSuccessfulMetadataDownload(metadataFile);
long contentId = prepareSuccessfulContentDownload(logListFile);
long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
setSuccessfulDownload(metadataId, metadataFile);
long contentId = mCertificateTransparencyDownloader.startContentDownload();
setSuccessfulDownload(contentId, logListFile);
when(mCertificateTransparencyInstaller.install(
eq(Config.COMPATIBILITY_VERSION), any(), anyString()))
.thenReturn(false);
@ -281,8 +280,10 @@ public class CertificateTransparencyDownloaderTest {
File logListFile = makeLogListFile("456");
File metadataFile = File.createTempFile("log_list-wrong_metadata", "sig");
mSignatureVerifier.setPublicKey(mPublicKey);
prepareSuccessfulMetadataDownload(metadataFile);
long contentId = prepareSuccessfulContentDownload(logListFile);
long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
setSuccessfulDownload(metadataId, metadataFile);
long contentId = mCertificateTransparencyDownloader.startContentDownload();
setSuccessfulDownload(contentId, logListFile);
assertNoVersionIsInstalled();
mCertificateTransparencyDownloader.onReceive(
@ -299,8 +300,10 @@ public class CertificateTransparencyDownloaderTest {
File logListFile = makeLogListFile("456");
File metadataFile = sign(logListFile);
mSignatureVerifier.resetPublicKey();
prepareSuccessfulMetadataDownload(metadataFile);
long contentId = prepareSuccessfulContentDownload(logListFile);
long metadataId = mCertificateTransparencyDownloader.startMetadataDownload();
setSuccessfulDownload(metadataId, metadataFile);
long contentId = mCertificateTransparencyDownloader.startContentDownload();
setSuccessfulDownload(contentId, logListFile);
assertNoVersionIsInstalled();
mCertificateTransparencyDownloader.onReceive(
@ -321,26 +324,23 @@ public class CertificateTransparencyDownloaderTest {
assertNoVersionIsInstalled();
// 1. Start download of public key.
String publicKeyUrl = "http://test-public-key.org";
long publicKeyId = preparePublicKeyDownload(publicKeyUrl);
mCertificateTransparencyDownloader.startPublicKeyDownload(publicKeyUrl);
long publicKeyId = mCertificateTransparencyDownloader.startPublicKeyDownload();
// 2. On successful public key download, set the key and start the metatadata download.
setSuccessfulDownload(publicKeyId, publicKeyFile);
long metadataId = prepareMetadataDownload("http://test-metadata.org");
mCertificateTransparencyDownloader.onReceive(
mContext, makeDownloadCompleteIntent(publicKeyId));
// 3. On successful metadata download, start the content download.
long metadataId = mCertificateTransparencyDownloader.getMetadataDownloadId();
setSuccessfulDownload(metadataId, metadataFile);
long contentId = prepareContentDownload("http://test-content.org");
mCertificateTransparencyDownloader.onReceive(
mContext, makeDownloadCompleteIntent(metadataId));
// 4. On successful content download, verify the signature and install the new version.
long contentId = mCertificateTransparencyDownloader.getContentDownloadId();
setSuccessfulDownload(contentId, logListFile);
when(mCertificateTransparencyInstaller.install(
eq(Config.COMPATIBILITY_VERSION), any(), anyString()))
@ -354,16 +354,10 @@ public class CertificateTransparencyDownloaderTest {
private void assertNoVersionIsInstalled() {
assertThat(mDataStore.getProperty(Config.VERSION)).isNull();
assertThat(mDataStore.getProperty(Config.CONTENT_URL)).isNull();
assertThat(mDataStore.getProperty(Config.METADATA_URL)).isNull();
}
private void assertInstallSuccessful(String version) {
assertThat(mDataStore.getProperty(Config.VERSION)).isEqualTo(version);
assertThat(mDataStore.getProperty(Config.CONTENT_URL))
.isEqualTo(mDataStore.getProperty(Config.CONTENT_URL_PENDING));
assertThat(mDataStore.getProperty(Config.METADATA_URL))
.isEqualTo(mDataStore.getProperty(Config.METADATA_URL_PENDING));
}
private Intent makeDownloadCompleteIntent(long downloadId) {
@ -371,110 +365,51 @@ public class CertificateTransparencyDownloaderTest {
.putExtra(DownloadManager.EXTRA_DOWNLOAD_ID, downloadId);
}
private long prepareDownloadId(String url) {
long downloadId = mNextDownloadId++;
when(mDownloadHelper.startDownload(url)).thenReturn(downloadId);
return downloadId;
private void prepareDataStore() {
mDataStore.load();
mDataStore.setProperty(Config.CONTENT_URL, Config.URL_LOG_LIST);
mDataStore.setProperty(Config.METADATA_URL, Config.URL_SIGNATURE);
mDataStore.setProperty(Config.PUBLIC_KEY_URL, Config.URL_PUBLIC_KEY);
}
private long preparePublicKeyDownload(String url) {
long downloadId = prepareDownloadId(url);
mDataStore.setProperty(Config.PUBLIC_KEY_URL_PENDING, url);
return downloadId;
private void prepareDownloadManager() {
when(mDownloadManager.enqueue(any(Request.class)))
.thenAnswer(invocation -> mNextDownloadId++);
}
private long prepareMetadataDownload(String url) {
long downloadId = prepareDownloadId(url);
mDataStore.setProperty(Config.METADATA_URL_PENDING, url);
return downloadId;
}
private long prepareContentDownload(String url) {
long downloadId = prepareDownloadId(url);
mDataStore.setProperty(Config.CONTENT_URL_PENDING, url);
return downloadId;
}
private long prepareSuccessfulDownload(String propertyKey) {
long downloadId = mNextDownloadId++;
mDataStore.setPropertyLong(propertyKey, downloadId);
when(mDownloadHelper.getDownloadStatus(downloadId))
.thenReturn(makeSuccessfulDownloadStatus(downloadId));
return downloadId;
}
private long prepareSuccessfulDownload(String propertyKey, File file) {
long downloadId = prepareSuccessfulDownload(propertyKey);
when(mDownloadHelper.getUri(downloadId)).thenReturn(Uri.fromFile(file));
return downloadId;
}
private long prepareSuccessfulPublicKeyDownload(File file) {
long downloadId = prepareSuccessfulDownload(Config.PUBLIC_KEY_URL_KEY, file);
mDataStore.setProperty(
Config.METADATA_URL_PENDING, "http://public-key-was-downloaded-here.org");
return downloadId;
}
private long prepareSuccessfulMetadataDownload(File file) {
long downloadId = prepareSuccessfulDownload(Config.METADATA_URL_KEY, file);
mDataStore.setProperty(
Config.METADATA_URL_PENDING, "http://metadata-was-downloaded-here.org");
return downloadId;
}
private long prepareSuccessfulContentDownload(File file) {
long downloadId = prepareSuccessfulDownload(Config.CONTENT_URL_KEY, file);
mDataStore.setProperty(
Config.CONTENT_URL_PENDING, "http://content-was-downloaded-here.org");
return downloadId;
private Cursor makeSuccessfulDownloadCursor() {
MatrixCursor cursor =
new MatrixCursor(
new String[] {
DownloadManager.COLUMN_STATUS, DownloadManager.COLUMN_REASON
});
cursor.addRow(new Object[] {DownloadManager.STATUS_SUCCESSFUL, -1});
return cursor;
}
private void setSuccessfulDownload(long downloadId, File file) {
when(mDownloadHelper.getDownloadStatus(downloadId))
.thenReturn(makeSuccessfulDownloadStatus(downloadId));
when(mDownloadHelper.getUri(downloadId)).thenReturn(Uri.fromFile(file));
when(mDownloadManager.query(any(Query.class))).thenReturn(makeSuccessfulDownloadCursor());
when(mDownloadManager.getUriForDownloadedFile(downloadId)).thenReturn(Uri.fromFile(file));
}
private long prepareFailedDownload(String propertyKey, int... downloadManagerErrors) {
long downloadId = mNextDownloadId++;
mDataStore.setPropertyLong(propertyKey, downloadId);
DownloadStatus firstError =
DownloadStatus.builder()
.setDownloadId(downloadId)
.setStatus(DownloadManager.STATUS_FAILED)
.setReason(downloadManagerErrors[0])
.build();
DownloadStatus[] otherErrors = new DownloadStatus[downloadManagerErrors.length - 1];
private Cursor makeFailedDownloadCursor(int error) {
MatrixCursor cursor =
new MatrixCursor(
new String[] {
DownloadManager.COLUMN_STATUS, DownloadManager.COLUMN_REASON
});
cursor.addRow(new Object[] {DownloadManager.STATUS_FAILED, error});
return cursor;
}
private void setFailedDownload(long downloadId, int... downloadManagerErrors) {
Cursor first = makeFailedDownloadCursor(downloadManagerErrors[0]);
Cursor[] others = new Cursor[downloadManagerErrors.length - 1];
for (int i = 1; i < downloadManagerErrors.length; i++) {
otherErrors[i - 1] =
DownloadStatus.builder()
.setDownloadId(downloadId)
.setStatus(DownloadManager.STATUS_FAILED)
.setReason(downloadManagerErrors[i])
.build();
others[i - 1] = makeFailedDownloadCursor(downloadManagerErrors[i]);
}
when(mDownloadHelper.getDownloadStatus(downloadId)).thenReturn(firstError, otherErrors);
return downloadId;
}
private long prepareFailedPublicKeyDownload(int... downloadManagerErrors) {
return prepareFailedDownload(Config.PUBLIC_KEY_URL_KEY, downloadManagerErrors);
}
private long prepareFailedMetadataDownload(int... downloadManagerErrors) {
return prepareFailedDownload(Config.METADATA_URL_KEY, downloadManagerErrors);
}
private long prepareFailedContentDownload(int... downloadManagerErrors) {
return prepareFailedDownload(Config.CONTENT_URL_KEY, downloadManagerErrors);
}
private DownloadStatus makeSuccessfulDownloadStatus(long downloadId) {
return DownloadStatus.builder()
.setDownloadId(downloadId)
.setStatus(DownloadManager.STATUS_SUCCESSFUL)
.build();
when(mDownloadManager.query(any())).thenReturn(first, others);
when(mDownloadManager.getUriForDownloadedFile(downloadId)).thenReturn(null);
}
private File writePublicKeyToFile(PublicKey publicKey)