Add CertificateDetailsPageProvider.

Test: Unit Test
Fix: 326191189
Change-Id: I542903b26bac589ba67c297d8758ea0a69ebdf23
This commit is contained in:
Charlotte Lu
2024-02-21 18:33:56 +08:00
parent c8d5701ab5
commit 458b942a90
5 changed files with 215 additions and 2 deletions

View File

@@ -112,6 +112,10 @@
android:title="@string/wifi_auto_connect_title"
android:summary="@string/wifi_auto_connect_summary"/>
<com.android.settings.spa.preference.ComposePreference
android:key="certificate_details"
settings:controller="com.android.settings.wifi.details2.CertificateDetailsPreferenceController"/>
<!-- Add device Preference -->
<Preference
android:key="add_device_to_network"

View File

@@ -22,11 +22,11 @@ import com.android.settings.network.apn.ApnEditPageProvider
import com.android.settings.spa.about.AboutPhonePageProvider
import com.android.settings.spa.app.AllAppListPageProvider
import com.android.settings.spa.app.AppsMainPageProvider
import com.android.settings.spa.app.battery.BatteryOptimizationModeAppListPageProvider
import com.android.settings.spa.app.appcompat.UserAspectRatioAppsPageProvider
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
import com.android.settings.spa.app.appinfo.CloneAppInfoSettingsProvider
import com.android.settings.spa.app.backgroundinstall.BackgroundInstalledAppsPageProvider
import com.android.settings.spa.app.battery.BatteryOptimizationModeAppListPageProvider
import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
import com.android.settings.spa.app.specialaccess.BackupTasksAppsListProvider

View File

@@ -55,6 +55,7 @@ import com.android.settings.wifi.WifiConfigUiBase2;
import com.android.settings.wifi.WifiDialog2;
import com.android.settings.wifi.WifiUtils;
import com.android.settings.wifi.details2.AddDevicePreferenceController2;
import com.android.settings.wifi.details2.CertificateDetailsPreferenceController;
import com.android.settings.wifi.details2.WifiAutoConnectPreferenceController2;
import com.android.settings.wifi.details2.WifiDetailPreferenceController2;
import com.android.settings.wifi.details2.WifiMeteredPreferenceController2;
@@ -122,8 +123,12 @@ public class WifiNetworkDetailsFragment extends RestrictedDashboardFragment impl
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
String wifiEntryKey = getArguments().getString(KEY_CHOSEN_WIFIENTRY_KEY);
setupNetworksDetailTracker();
use(WifiPrivacyPreferenceController.class)
.setWifiEntryKey(getArguments().getString(KEY_CHOSEN_WIFIENTRY_KEY));
.setWifiEntryKey(wifiEntryKey);
use(CertificateDetailsPreferenceController.class)
.setWifiEntry(mNetworkDetailsTracker.getWifiEntry());
}
@Override

View File

@@ -0,0 +1,131 @@
/*
* 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.settings.wifi.details2
import android.content.Context
import android.content.DialogInterface
import android.net.http.SslCertificate
import android.security.KeyChain
import android.security.keystore.KeyProperties
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter
import android.util.Log
import android.view.View
import android.widget.ArrayAdapter
import android.widget.LinearLayout
import android.widget.Spinner
import androidx.appcompat.app.AlertDialog
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.spa.preference.ComposePreferenceController
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 java.security.KeyStore
import java.security.cert.X509Certificate
class CertificateDetailsPreferenceController(context: Context, preferenceKey: String) :
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
else CONDITIONALLY_UNAVAILABLE
}
@Composable
override fun Content() {
CertificateDetails()
}
@Composable
fun CertificateDetails() {
val context = LocalContext.current
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) }
})
}
private fun getCertX509(wifiEntry: WifiEntry): Boolean {
if (certX509 != null ) return true
certificateAliases =
wifiEntry.wifiConfiguration?.enterpriseConfig?.caCertificateAliases?.get(0)
?: return false
return try {
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(AndroidKeyStoreLoadStoreParameter(KeyProperties.NAMESPACE_WIFI))
val cert = keyStore.getCertificate(certificateAliases)
certX509 = KeyChain.toCertificate(cert.encoded)
true
} catch (e: Exception) {
Log.e(TAG, "Failed to open Android Keystore.", e)
false
}
}
private fun createCertificateDetailsDialog(context: Context, certX509: X509Certificate) {
val listener =
DialogInterface.OnClickListener { dialog, id ->
dialog.dismiss()
}
val titles = ArrayList<String>()
val sslCert = SslCertificate(certX509)
titles.add(sslCert.issuedTo.cName)
val arrayAdapter = ArrayAdapter(
context,
android.R.layout.simple_spinner_item,
titles
)
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
val spinner = Spinner(context)
spinner.setAdapter(arrayAdapter)
val certLayout = LinearLayout(context)
certLayout.orientation = LinearLayout.VERTICAL
// Prevent content overlapping with spinner
certLayout.setClipChildren(true)
certLayout.addView(spinner)
val view = sslCert.inflateCertificateView(context)
view.visibility = View.VISIBLE
certLayout.addView(view)
certLayout.visibility = View.VISIBLE
val dialog = AlertDialog.Builder(context)
.setView(certLayout)
.setTitle(com.android.internal.R.string.ssl_certificate)
.setPositiveButton(R.string.wifi_settings_ssid_block_button_close, null)
.setNegativeButton(R.string.trusted_credentials_remove_label, listener).create()
dialog.show()
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(false)
}
companion object {
const val TAG = "CertificateDetailsPreferenceController"
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.settings.wifi.details2
import android.content.Context
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
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 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.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
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)
@Before
fun setUp() {
controller.certificateAliases = MOCK_CA
controller.certX509 = mockCertX509
}
@Test
fun title_isDisplayed() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
controller.Content()
}
}
composeTestRule.onNodeWithText(context.getString(com.android.internal.R.string.ssl_certificate))
.assertIsDisplayed()
}
private companion object {
const val TEST_KEY = "test_key"
const val MOCK_CA = "mock_ca"
}
}