Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 233c84f6 authored by Charlotte Lu's avatar Charlotte Lu Committed by Android (Google) Code Review
Browse files

Merge "[WIFI-Enterprise] Change the summary for Certificate Details." into main

parents 823d1870 eab9765e
Loading
Loading
Loading
Loading
+48 −12
Original line number Diff line number Diff line
@@ -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"
    }
+29 −8
Original line number Diff line number Diff line
@@ -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<X509Certificate> {}

    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
        doNothing().whenever(mock).startActivity(any())
    }

    private val controller = CertificateDetailsPreferenceController(context, TEST_KEY)

    private val mockCertificateInfo = mock<WifiEntry.CertificateInfo> {
        it.validationMethod =
            WifiEntry.CertificateInfo.CERTIFICATE_VALIDATION_METHOD_USING_INSTALLED_ROOTCA
        it.caCertificateAliases = arrayOf(MOCK_CA)
    }
    private val mockWifiEntry =
        mock<WifiEntry> { 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 {