Merge "bpf: Test bitmap open (duplicates GTS)" into main
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/CertificateTransparencyJob.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyJob.java
index bf23cb0..abede87 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyJob.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyJob.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Build;
+import android.os.ConfigUpdate;
 import android.os.SystemClock;
 import android.util.Log;
 
@@ -32,13 +33,13 @@
 
     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;
     private final AlarmManager mAlarmManager;
 
+    private boolean mDependenciesReady = false;
+
     /** Creates a new {@link CertificateTransparencyJob} object. */
     public CertificateTransparencyJob(
             Context context,
@@ -51,17 +52,19 @@
     }
 
     void initialize() {
-        mDataStore.load();
-        mCertificateTransparencyDownloader.initialize();
-
         mContext.registerReceiver(
-                this, new IntentFilter(ACTION_JOB_START), Context.RECEIVER_EXPORTED);
+                this,
+                new IntentFilter(ConfigUpdate.ACTION_UPDATE_CT_LOGS),
+                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));
+                        mContext,
+                        0,
+                        new Intent(ConfigUpdate.ACTION_UPDATE_CT_LOGS),
+                        PendingIntent.FLAG_IMMUTABLE));
 
         if (Config.DEBUG) {
             Log.d(TAG, "CertificateTransparencyJob scheduled successfully.");
@@ -70,13 +73,18 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (!ACTION_JOB_START.equals(intent.getAction())) {
+        if (!ConfigUpdate.ACTION_UPDATE_CT_LOGS.equals(intent.getAction())) {
             Log.w(TAG, "Received unexpected broadcast with action " + intent);
             return;
         }
         if (Config.DEBUG) {
             Log.d(TAG, "Starting CT daily job.");
         }
+        if (!mDependenciesReady) {
+            mDataStore.load();
+            mCertificateTransparencyDownloader.initialize();
+            mDependenciesReady = true;
+        }
 
         mDataStore.setProperty(Config.CONTENT_URL, Config.URL_LOG_LIST);
         mDataStore.setProperty(Config.METADATA_URL, Config.URL_SIGNATURE);
diff --git a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
index 782e6b5..6151727 100644
--- a/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
+++ b/networksecurity/service/src/com/android/server/net/ct/CertificateTransparencyService.java
@@ -43,7 +43,7 @@
         return DeviceConfig.getBoolean(
                         Config.NAMESPACE_NETWORK_SECURITY,
                         Config.FLAG_SERVICE_ENABLED,
-                        /* defaultValue= */ false)
+                        /* defaultValue= */ true)
                 && certificateTransparencyService()
                 && certificateTransparencyConfiguration();
     }
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");
diff --git a/staticlibs/device/com/android/net/module/util/TimerFdUtils.java b/staticlibs/device/com/android/net/module/util/TimerFdUtils.java
index c7ed911..f0de142 100644
--- a/staticlibs/device/com/android/net/module/util/TimerFdUtils.java
+++ b/staticlibs/device/com/android/net/module/util/TimerFdUtils.java
@@ -68,9 +68,9 @@
     /**
      * Set expiration time to timerfd
      */
-    static boolean setExpirationTime(int id, long expirationTimeMs) {
+    static boolean setExpirationTime(int fd, long expirationTimeMs) {
         try {
-            setTime(id, expirationTimeMs);
+            setTime(fd, expirationTimeMs);
         } catch (IOException e) {
             Log.e(TAG, "setExpirationTime failed", e);
             return false;
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index feb4621..9457a42 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -1615,7 +1615,7 @@
         assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
         final ContentResolver resolver = mContext.getContentResolver();
         mCtsNetUtils.ensureWifiConnected();
-        final String ssid = unquoteSSID(mWifiManager.getConnectionInfo().getSSID());
+        final String ssid = unquoteSSID(getSSID());
         final String oldMeteredSetting = getWifiMeteredStatus(ssid);
         final String oldMeteredMultipathPreference = Settings.Global.getString(
                 resolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
@@ -1628,7 +1628,7 @@
             // since R.
             final Network network = setWifiMeteredStatusAndWait(ssid, true /* isMetered */,
                     false /* waitForValidation */);
-            assertEquals(ssid, unquoteSSID(mWifiManager.getConnectionInfo().getSSID()));
+            assertEquals(ssid, unquoteSSID(getSSID()));
             assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
                     NET_CAPABILITY_NOT_METERED), false);
             assertMultipathPreferenceIsEventually(network, initialMeteredPreference,
@@ -2429,7 +2429,7 @@
                 mPackageManager.hasSystemFeature(FEATURE_WIFI));
 
         final Network network = mCtsNetUtils.ensureWifiConnected();
-        final String ssid = unquoteSSID(mWifiManager.getConnectionInfo().getSSID());
+        final String ssid = unquoteSSID(getSSID());
         assertNotNull("Ssid getting from WifiManager is null", ssid);
         // This package should have no NETWORK_SETTINGS permission. Verify that no ssid is contained
         // in the NetworkCapabilities.
@@ -2940,6 +2940,15 @@
                         new Handler(Looper.getMainLooper())), NETWORK_SETTINGS);
     }
 
+    /**
+     * It needs android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
+     * to use WifiManager.getConnectionInfo() on the visible background user.
+     */
+    private String getSSID() {
+        return runWithShellPermissionIdentity(() ->
+                mWifiManager.getConnectionInfo().getSSID());
+    }
+
     private static final class OnCompleteListenerCallback {
         final CompletableFuture<Object> mDone = new CompletableFuture<>();