diff --git a/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceController.kt b/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceController.kt index 8e1fa23fdb8..80e41e2fbd3 100644 --- a/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceController.kt +++ b/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceController.kt @@ -37,6 +37,9 @@ import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.wifi.flags.Flags import com.android.wifitrackerlib.WifiEntry +import com.android.wifitrackerlib.WifiEntry.CertificateInfo.CERTIFICATE_VALIDATION_METHOD_USING_CERTIFICATE_PINNING +import com.android.wifitrackerlib.WifiEntry.CertificateInfo.CERTIFICATE_VALIDATION_METHOD_USING_INSTALLED_ROOTCA +import com.android.wifitrackerlib.WifiEntry.CertificateInfo.CERTIFICATE_VALIDATION_METHOD_USING_SYSTEM_CERTIFICATE import java.security.KeyStore import java.security.cert.X509Certificate @@ -44,15 +47,13 @@ class CertificateDetailsPreferenceController(context: Context, preferenceKey: St ComposePreferenceController(context, preferenceKey) { private lateinit var wifiEntry: WifiEntry - lateinit var certificateAliases: String - lateinit var certX509: X509Certificate fun setWifiEntry(entry: WifiEntry) { wifiEntry = entry } override fun getAvailabilityStatus(): Int { - return if (Flags.androidVWifiApi() && getCertX509(wifiEntry)) AVAILABLE + return if (Flags.androidVWifiApi() && isCertificateDetailsAvailable(wifiEntry)) AVAILABLE else CONDITIONALLY_UNAVAILABLE } @@ -64,26 +65,52 @@ class CertificateDetailsPreferenceController(context: Context, preferenceKey: St @Composable fun CertificateDetails() { val context = LocalContext.current + + val validationMethod = wifiEntry.certificateInfo!!.validationMethod + val certificateDetailsSummary = when (validationMethod) { + CERTIFICATE_VALIDATION_METHOD_USING_SYSTEM_CERTIFICATE -> + stringResource(R.string.wifi_certificate_summary_system) + + CERTIFICATE_VALIDATION_METHOD_USING_INSTALLED_ROOTCA -> { + val aliasesSize = wifiEntry.certificateInfo?.caCertificateAliases?.size + if (aliasesSize == 1) stringResource(R.string.one_cacrt) + else + String.format( + stringResource(R.string.wifi_certificate_summary_Certificates), + aliasesSize + ) + } + + else -> stringResource(R.string.wifi_certificate_summary_pinning) + } + Preference(object : PreferenceModel { override val title = stringResource(com.android.internal.R.string.ssl_certificate) - override val summary = { certificateAliases } - override val onClick: () -> Unit = { createCertificateDetailsDialog(context, certX509) } + override val summary = { certificateDetailsSummary } + override val onClick: () -> Unit = { + if (validationMethod == CERTIFICATE_VALIDATION_METHOD_USING_INSTALLED_ROOTCA) + getCertX509(wifiEntry)?.let { + createCertificateDetailsDialog( + context, + it + ) + } + } }) } - private fun getCertX509(wifiEntry: WifiEntry): Boolean { - certificateAliases = - wifiEntry.wifiConfiguration?.enterpriseConfig?.caCertificateAliases?.get(0) - ?: return false + private fun getCertX509(wifiEntry: WifiEntry): X509Certificate? { + val certificateAliases = + wifiEntry.certificateInfo?.caCertificateAliases?.get(0) + ?: return null return try { val keyStore = KeyStore.getInstance("AndroidKeyStore") keyStore.load(AndroidKeyStoreLoadStoreParameter(KeyProperties.NAMESPACE_WIFI)) val cert = keyStore.getCertificate(certificateAliases) - certX509 = KeyChain.toCertificate(cert.encoded) - true + KeyChain.toCertificate(cert.encoded) } catch (e: Exception) { Log.e(TAG, "Failed to open Android Keystore.", e) - false + null } } @@ -124,6 +151,15 @@ class CertificateDetailsPreferenceController(context: Context, preferenceKey: St dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false) } + private fun isCertificateDetailsAvailable(wifiEntry: WifiEntry): Boolean { + val validationMethod = wifiEntry.certificateInfo?.validationMethod + return validationMethod in listOf( + CERTIFICATE_VALIDATION_METHOD_USING_SYSTEM_CERTIFICATE, + CERTIFICATE_VALIDATION_METHOD_USING_INSTALLED_ROOTCA, + CERTIFICATE_VALIDATION_METHOD_USING_CERTIFICATE_PINNING + ) + } + companion object { const val TAG = "CertificateDetailsPreferenceController" } diff --git a/tests/spa_unit/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceControllerTest.kt index 149937496ff..d0119dfcc05 100644 --- a/tests/spa_unit/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceControllerTest.kt +++ b/tests/spa_unit/src/com/android/settings/wifi/details2/CertificateDetailsPreferenceControllerTest.kt @@ -17,6 +17,7 @@ package com.android.settings.wifi.details2 import android.content.Context +import android.platform.test.annotations.RequiresFlagsEnabled import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.test.assertIsDisplayed @@ -24,13 +25,15 @@ import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import java.security.cert.X509Certificate +import com.android.settings.R +import com.android.wifitrackerlib.WifiEntry import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.doNothing +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.whenever @@ -40,21 +43,26 @@ class CertificateDetailsPreferenceControllerTest { @get:Rule val composeTestRule = createComposeRule() - private val mockCertX509 = mock {} - private val context: Context = spy(ApplicationProvider.getApplicationContext()) { doNothing().whenever(mock).startActivity(any()) } - private val controller = CertificateDetailsPreferenceController(context, TEST_KEY) + private val mockCertificateInfo = mock { + it.validationMethod = + WifiEntry.CertificateInfo.CERTIFICATE_VALIDATION_METHOD_USING_INSTALLED_ROOTCA + it.caCertificateAliases = arrayOf(MOCK_CA) + } + private val mockWifiEntry = + mock { on { certificateInfo } doReturn mockCertificateInfo } + @Before fun setUp() { - controller.certificateAliases = MOCK_CA - controller.certX509 = mockCertX509 + controller.setWifiEntry(mockWifiEntry) } @Test + @RequiresFlagsEnabled(com.android.wifi.flags.Flags.FLAG_ANDROID_V_WIFI_API) fun title_isDisplayed() { composeTestRule.setContent { CompositionLocalProvider(LocalContext provides context) { @@ -62,8 +70,21 @@ class CertificateDetailsPreferenceControllerTest { } } - composeTestRule.onNodeWithText(context.getString(com.android.internal.R.string.ssl_certificate)) - .assertIsDisplayed() + composeTestRule.onNodeWithText( + context.getString(com.android.internal.R.string.ssl_certificate) + ).assertIsDisplayed() + } + + @Test + @RequiresFlagsEnabled(com.android.wifi.flags.Flags.FLAG_ANDROID_V_WIFI_API) + fun one_caCertificate_summary() { + composeTestRule.setContent { + CompositionLocalProvider(LocalContext provides context) { + controller.Content() + } + } + + composeTestRule.onNodeWithText(context.getString(R.string.one_cacrt)).assertIsDisplayed() } private companion object {