Add daily CertificateTransparencyJob

Following a conversation with the Experiments team (b/374692404), it
appears we will not be able to use device config flags to trigger the
download of CT log lists.

This CL is the first of a series that will allow us to move away from
flags and instead rely on a daily job execution. For scheduling we rely
on the Android AlarmManager.

I previously attempted to schedule the daily job using the Android
JobScheduler. However, those attempts were unsuccessful beacause the
Tethering apex is not on the system_server classpath, so the system
would crash when attempting to load the class to execute the job.

Flag: com.android.net.ct.flags.certificate_transparency_job
Bug: 374692404
Test: NetworkSecurityUnitTests
Change-Id: I6ce44dcea464cab20d2df73b065e35c0f76dcbab
This commit is contained in:
Sandro Montanari 2024-11-19 16:03:54 +00:00
commit 70c4de8edc
5 changed files with 129 additions and 13 deletions

View file

@ -8,3 +8,12 @@ flag {
bug: "319829948"
is_fixed_read_only: true
}
flag {
name: "certificate_transparency_job"
is_exported: true
namespace: "network_security"
description: "Enable daily job service for certificate transparency instead of flags listener"
bug: "319829948"
is_fixed_read_only: true
}

View file

@ -88,12 +88,17 @@ class CertificateTransparencyDownloader extends BroadcastReceiver {
}
void setPublicKey(String publicKey) throws GeneralSecurityException {
mPublicKey =
Optional.of(
KeyFactory.getInstance("RSA")
.generatePublic(
new X509EncodedKeySpec(
Base64.getDecoder().decode(publicKey))));
try {
mPublicKey =
Optional.of(
KeyFactory.getInstance("RSA")
.generatePublic(
new X509EncodedKeySpec(
Base64.getDecoder().decode(publicKey))));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Invalid public key Base64 encoding", e);
mPublicKey = Optional.empty();
}
}
@VisibleForTesting

View file

@ -16,7 +16,6 @@
package com.android.server.net.ct;
import android.annotation.RequiresApi;
import android.content.Context;
import android.os.Build;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
@ -35,10 +34,11 @@ class CertificateTransparencyFlagsListener implements DeviceConfig.OnPropertiesC
private final DataStore mDataStore;
private final CertificateTransparencyDownloader mCertificateTransparencyDownloader;
CertificateTransparencyFlagsListener(Context context) {
mDataStore = new DataStore(Config.PREFERENCES_FILE);
mCertificateTransparencyDownloader =
new CertificateTransparencyDownloader(context, mDataStore);
CertificateTransparencyFlagsListener(
DataStore dataStore,
CertificateTransparencyDownloader certificateTransparencyDownloader) {
mDataStore = dataStore;
mCertificateTransparencyDownloader = certificateTransparencyDownloader;
}
void initialize() {

View file

@ -0,0 +1,87 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.net.ct;
import android.annotation.RequiresApi;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.SystemClock;
import android.provider.DeviceConfig;
import android.util.Log;
import java.util.HashMap;
/** Implementation of the Certificate Transparency job */
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class CertificateTransparencyJob extends BroadcastReceiver {
private static final String TAG = "CertificateTransparencyJob";
private static final String ACTION_JOB_START = "com.android.server.net.ct.action.JOB_START";
private final Context mContext;
private final DataStore mDataStore;
private final CertificateTransparencyDownloader mCertificateTransparencyDownloader;
// TODO(b/374692404): remove dependency to flags.
private final CertificateTransparencyFlagsListener mFlagsListener;
private final AlarmManager mAlarmManager;
/** Creates a new {@link CertificateTransparencyJob} object. */
public CertificateTransparencyJob(
Context context,
DataStore dataStore,
CertificateTransparencyDownloader certificateTransparencyDownloader,
CertificateTransparencyFlagsListener flagsListener) {
mContext = context;
mFlagsListener = flagsListener;
mDataStore = dataStore;
mCertificateTransparencyDownloader = certificateTransparencyDownloader;
mAlarmManager = context.getSystemService(AlarmManager.class);
}
void initialize() {
mDataStore.load();
mCertificateTransparencyDownloader.initialize();
mContext.registerReceiver(
this, new IntentFilter(ACTION_JOB_START), Context.RECEIVER_EXPORTED);
mAlarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime(), // schedule first job at earliest convenient time.
AlarmManager.INTERVAL_DAY,
PendingIntent.getBroadcast(
mContext, 0, new Intent(ACTION_JOB_START), PendingIntent.FLAG_IMMUTABLE));
if (Config.DEBUG) {
Log.d(TAG, "CertificateTransparencyJob scheduled successfully.");
}
}
@Override
public void onReceive(Context context, Intent intent) {
if (!ACTION_JOB_START.equals(intent.getAction())) {
Log.w(TAG, "Received unexpected broadcast with action " + intent);
return;
}
mFlagsListener.onPropertiesChanged(
new DeviceConfig.Properties(Config.NAMESPACE_NETWORK_SECURITY, new HashMap<>()));
}
}

View file

@ -28,7 +28,10 @@ import com.android.server.SystemService;
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public class CertificateTransparencyService extends ICertificateTransparencyManager.Stub {
private final DataStore mDataStore;
private final CertificateTransparencyDownloader mCertificateTransparencyDownloader;
private final CertificateTransparencyFlagsListener mFlagsListener;
private final CertificateTransparencyJob mCertificateTransparencyJob;
/**
* @return true if the CertificateTransparency service is enabled.
@ -41,7 +44,15 @@ public class CertificateTransparencyService extends ICertificateTransparencyMana
/** Creates a new {@link CertificateTransparencyService} object. */
public CertificateTransparencyService(Context context) {
mFlagsListener = new CertificateTransparencyFlagsListener(context);
mDataStore = new DataStore(Config.PREFERENCES_FILE);
mCertificateTransparencyDownloader =
new CertificateTransparencyDownloader(context, mDataStore);
mFlagsListener =
new CertificateTransparencyFlagsListener(
mDataStore, mCertificateTransparencyDownloader);
mCertificateTransparencyJob =
new CertificateTransparencyJob(
context, mDataStore, mCertificateTransparencyDownloader, mFlagsListener);
}
/**
@ -53,7 +64,11 @@ public class CertificateTransparencyService extends ICertificateTransparencyMana
switch (phase) {
case SystemService.PHASE_BOOT_COMPLETED:
mFlagsListener.initialize();
if (Flags.certificateTransparencyJob()) {
mCertificateTransparencyJob.initialize();
} else {
mFlagsListener.initialize();
}
break;
default:
}