diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2ba87d7099a..02dd9cdf393 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -5036,6 +5036,12 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 35c175997c0..776e3f41093 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3323,6 +3323,8 @@
Cancel
+
+ Duplicate apn entry.
The Name field can\u2019t be empty.
diff --git a/src/com/android/settings/accessibility/HearingDevicePairingFragment.java b/src/com/android/settings/accessibility/HearingDevicePairingFragment.java
index fb79ece55bf..78f5b4c1b6e 100644
--- a/src/com/android/settings/accessibility/HearingDevicePairingFragment.java
+++ b/src/com/android/settings/accessibility/HearingDevicePairingFragment.java
@@ -149,6 +149,7 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im
for (BluetoothGatt gatt: mConnectingGattList) {
gatt.disconnect();
}
+ mConnectingGattList.clear();
mLocalManager.setForegroundActivity(null);
mLocalManager.getEventManager().unregisterCallback(this);
}
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index f14c32c5381..0da1034994c 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -89,6 +89,7 @@ import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.LayoutPreference;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.settingslib.wifi.WifiSavedConfigUtils;
+import com.android.wifi.flags.Flags;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiEntry.ConnectCallback;
import com.android.wifitrackerlib.WifiPickerTracker;
@@ -1257,8 +1258,19 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
// If it's an unsaved secure WifiEntry, it will callback
// ConnectCallback#onConnectResult with ConnectCallback#CONNECT_STATUS_FAILURE_NO_CONFIG
- wifiEntry.connect(new WifiEntryConnectCallback(wifiEntry, editIfNoConfig,
- fullScreenEdit));
+ WifiEntryConnectCallback callback =
+ new WifiEntryConnectCallback(wifiEntry, editIfNoConfig, fullScreenEdit);
+
+ if (Flags.wepUsage() && wifiEntry.getSecurityTypes().contains(WifiEntry.SECURITY_WEP)) {
+ WepNetworkDialogActivity.checkWepAllowed(
+ getContext(), getViewLifecycleOwner(), wifiEntry.getSsid(), () -> {
+ wifiEntry.connect(callback);
+ return null;
+ });
+ return;
+ }
+
+ wifiEntry.connect(callback);
}
private class WifiConnectActionListener implements WifiManager.ActionListener {
diff --git a/src/com/android/settings/network/WepNetworkDialogActivity.kt b/src/com/android/settings/network/WepNetworkDialogActivity.kt
new file mode 100644
index 00000000000..2fa87849ef1
--- /dev/null
+++ b/src/com/android/settings/network/WepNetworkDialogActivity.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.network
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.Intent
+import android.net.wifi.WifiManager
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.style.TextAlign
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import com.android.settings.R
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.wifi.ConfigureWifiSettings
+import com.android.settingslib.spa.SpaBaseDialogActivity
+import com.android.settingslib.spa.widget.dialog.AlertDialogButton
+import com.android.settingslib.spa.widget.dialog.SettingsAlertDialogWithIcon
+import kotlin.coroutines.resume
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+
+class WepNetworkDialogActivity : SpaBaseDialogActivity() {
+ @Composable
+ override fun Content() {
+ val context = LocalContext.current
+ val wifiManager = context.getSystemService(WifiManager::class.java)
+ SettingsAlertDialogWithIcon(
+ onDismissRequest = { finish() },
+ confirmButton = AlertDialogButton(
+ getString(R.string.wifi_settings_ssid_block_button_close)
+ ) { finish() },
+ dismissButton = AlertDialogButton(
+ getString(R.string.wifi_settings_wep_networks_button_allow)
+ ) {
+ SubSettingLauncher(context)
+ .setTitleText(context.getText(R.string.network_and_internet_preferences_title))
+ .setSourceMetricsCategory(SettingsEnums.CONFIGURE_WIFI)
+ .setDestination(ConfigureWifiSettings::class.java.getName())
+ .launch()
+ finish()
+ },
+ title = String.format(
+ getString(R.string.wifi_settings_wep_networks_blocked_title),
+ intent.getStringExtra(SSID) ?: SSID
+ ),
+ text = {
+ Text(
+ if (wifiManager?.isWepSupported == false)
+ getString(R.string.wifi_settings_wep_networks_summary_toggle_off)
+ else getString(R.string.wifi_settings_wep_networks_summary_blocked_by_carrier),
+ modifier = Modifier.fillMaxWidth(),
+ textAlign = TextAlign.Center
+ )
+ })
+ }
+
+ companion object {
+ @JvmStatic
+ fun checkWepAllowed(
+ context: Context,
+ lifecycleOwner: LifecycleOwner,
+ ssid: String,
+ onAllowed: () -> Unit,
+ ) {
+ lifecycleOwner.lifecycleScope.launch {
+ val wifiManager = context.getSystemService(WifiManager::class.java) ?: return@launch
+ if (wifiManager.queryWepAllowed()) {
+ onAllowed()
+ } else {
+ val intent = Intent(context, WepNetworkDialogActivity::class.java).apply {
+ putExtra(SSID, ssid)
+ }
+ context.startActivity(intent)
+ }
+ }
+ }
+
+ private suspend fun WifiManager.queryWepAllowed(): Boolean =
+ withContext(Dispatchers.Default) {
+ suspendCancellableCoroutine { continuation ->
+ queryWepAllowed(Dispatchers.Default.asExecutor()) {
+ continuation.resume(it)
+ }
+ }
+ }
+
+ const val SSID = "ssid"
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index 2600618caae..924e311ffdc 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -19,16 +19,19 @@ package com.android.settings.network.apn
import android.net.Uri
import android.os.Bundle
import androidx.compose.foundation.layout.Column
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.Done
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource
@@ -39,14 +42,14 @@ import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeDisplayNam
import com.android.settings.network.apn.ApnNetworkTypes.getNetworkTypeSelectedOptionsState
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.LocalNavController
+import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuBox
import com.android.settingslib.spa.widget.editor.SettingsExposedDropdownMenuCheckBox
import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
import com.android.settingslib.spa.widget.editor.SettingsTextFieldPassword
-import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import java.util.Base64
@@ -98,25 +101,59 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState, uriInit: Ur
getNetworkTypeSelectedOptionsState(apnData.networkType)
}
val navController = LocalNavController.current
+ var valid: String?
RegularScaffold(
title = if (apnDataInit.newApn) stringResource(id = R.string.apn_add) else stringResource(id = R.string.apn_edit),
actions = {
if (!apnData.customizedConfig.readOnlyApn) {
- IconButton(onClick = {
- if (!apnData.validEnabled) apnData = apnData.copy(validEnabled = true)
- val valid = validateAndSaveApnData(
+ Button(onClick = {
+ apnData = apnData.copy(
+ networkType = ApnNetworkTypes.getNetworkType(
+ networkTypeSelectedOptionsState
+ )
+ )
+ valid = validateAndSaveApnData(
apnDataInit,
apnData,
context,
- uriInit,
- networkTypeSelectedOptionsState
+ uriInit
)
- if (valid) navController.navigateBack()
- }) { Icon(imageVector = Icons.Outlined.Done, contentDescription = null) }
+ if (valid == null) navController.navigateBack()
+ else if (!apnData.validEnabled) apnData = apnData.copy(validEnabled = true)
+ }) { Text(text = stringResource(id = R.string.save)) }
+ }
+ if (!apnData.newApn && !apnData.customizedConfig.readOnlyApn
+ && apnData.customizedConfig.isAddApnAllowed
+ ) {
+ MoreOptionsAction {
+ DropdownMenuItem(
+ text = { Text(stringResource(R.string.menu_delete)) },
+ onClick = {
+ deleteApn(uriInit, context)
+ navController.navigateBack()
+ })
+ }
}
},
) {
Column {
+ if (apnData.validEnabled) {
+ apnData = apnData.copy(
+ networkType = ApnNetworkTypes.getNetworkType(
+ networkTypeSelectedOptionsState
+ )
+ )
+ valid = validateApnData(uriInit, apnData, context)
+ valid?.let {
+ Text(
+ text = it,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(SettingsDimension.menuFieldPadding),
+ color = MaterialTheme.colorScheme.primary
+ )
+ }
+ }
SettingsOutlinedTextField(
value = apnData.name,
label = stringResource(R.string.apn_name),
@@ -214,19 +251,6 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState, uriInit: Ur
emptyVal = stringResource(R.string.network_type_unspecified),
enabled = apnData.networkTypeEnabled
) {}
- if (!apnData.newApn && !apnData.customizedConfig.readOnlyApn
- && apnData.customizedConfig.isAddApnAllowed
- ) {
- Preference(
- object : PreferenceModel {
- override val title = stringResource(R.string.menu_delete)
- override val onClick = {
- deleteApn(uriInit, context)
- navController.navigateBack()
- }
- }
- )
- }
}
}
}
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnRepository.kt b/src/com/android/settings/network/apn/ApnRepository.kt
index e0121b4926a..2f16e693adb 100644
--- a/src/com/android/settings/network/apn/ApnRepository.kt
+++ b/src/com/android/settings/network/apn/ApnRepository.kt
@@ -20,6 +20,7 @@ import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.provider.Telephony
+import android.telephony.TelephonyManager
import android.util.Log
import com.android.settings.R
import com.android.settingslib.utils.ThreadUtils
@@ -150,7 +151,6 @@ fun getApnDataFromUri(uri: Uri, context: Context): ApnData {
private fun convertProtocol2Options(raw: String, context: Context): String {
val apnProtocolOptions = context.resources.getStringArray(R.array.apn_protocol_entries).toList()
val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
-
var uRaw = raw.uppercase(Locale.getDefault())
uRaw = if (uRaw == "IPV4") "IP" else uRaw
val protocolIndex = apnProtocolValues.indexOf(uRaw)
@@ -167,7 +167,6 @@ private fun convertProtocol2Options(raw: String, context: Context): String {
fun convertOptions2Protocol(protocolIndex: Int, context: Context): String {
val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
-
return if (protocolIndex == -1) {
""
} else {
@@ -179,7 +178,12 @@ fun convertOptions2Protocol(protocolIndex: Int, context: Context): String {
}
}
-fun updateApnDataToDatabase(newApn: Boolean, values: ContentValues, context: Context, uriInit: Uri) {
+fun updateApnDataToDatabase(
+ newApn: Boolean,
+ values: ContentValues,
+ context: Context,
+ uriInit: Uri
+) {
ThreadUtils.postOnBackgroundThread {
if (newApn) {
// Add a new apn to the database
@@ -194,4 +198,24 @@ fun updateApnDataToDatabase(newApn: Boolean, values: ContentValues, context: Con
)
}
}
+}
+
+fun isItemExist(uri: Uri, apnData: ApnData, context: Context): String? {
+ val contentValueMap = apnData.getContentValueMap(context)
+ contentValueMap.remove(Telephony.Carriers.CARRIER_ENABLED)
+ val list = contentValueMap.entries.toList()
+ val selection = list.joinToString(" AND ") { "${it.key} = ?" }
+ val selectionArgs: Array = list.map { it.value.toString() }.toTypedArray()
+ context.contentResolver.query(
+ uri,
+ sProjection,
+ selection /* selection */,
+ selectionArgs /* selectionArgs */,
+ null /* sortOrder */
+ )?.use { cursor ->
+ if (cursor.count > 0) {
+ return context.resources.getString(R.string.error_duplicate_apn_entry)
+ }
+ }
+ return null
}
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index e4cb603fcb7..6f39305f3cf 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -72,41 +72,38 @@ data class ApnData(
val validEnabled: Boolean = false,
val customizedConfig: CustomizedConfig = CustomizedConfig()
) {
+ fun getContentValueMap(context: Context): MutableMap {
+ val simCarrierId =
+ context.getSystemService(TelephonyManager::class.java)!!
+ .createForSubscriptionId(subId)
+ .getSimCarrierId()
+ return mutableMapOf(
+ Telephony.Carriers.NAME to name, Telephony.Carriers.APN to apn,
+ Telephony.Carriers.PROXY to proxy, Telephony.Carriers.PORT to port,
+ Telephony.Carriers.MMSPROXY to mmsProxy, Telephony.Carriers.MMSPORT to mmsPort,
+ Telephony.Carriers.USER to userName, Telephony.Carriers.SERVER to server,
+ Telephony.Carriers.PASSWORD to passWord, Telephony.Carriers.MMSC to mmsc,
+ Telephony.Carriers.AUTH_TYPE to authType,
+ Telephony.Carriers.PROTOCOL to convertOptions2Protocol(apnProtocol, context),
+ Telephony.Carriers.ROAMING_PROTOCOL to convertOptions2Protocol(apnRoaming, context),
+ Telephony.Carriers.TYPE to apnType,
+ Telephony.Carriers.NETWORK_TYPE_BITMASK to networkType,
+ Telephony.Carriers.CARRIER_ENABLED to apnEnable,
+ Telephony.Carriers.EDITED_STATUS to Telephony.Carriers.USER_EDITED,
+ Telephony.Carriers.CARRIER_ID to simCarrierId
+ )
+ }
+
fun getContentValues(context: Context): ContentValues {
val values = ContentValues()
- values.put(Telephony.Carriers.NAME, name)
- values.put(Telephony.Carriers.APN, apn)
- values.put(Telephony.Carriers.PROXY, proxy)
- values.put(Telephony.Carriers.PORT, port)
- values.put(Telephony.Carriers.MMSPROXY, mmsProxy)
- values.put(Telephony.Carriers.MMSPORT, mmsPort)
- values.put(Telephony.Carriers.USER, userName)
- values.put(Telephony.Carriers.SERVER, server)
- values.put(Telephony.Carriers.PASSWORD, passWord)
- values.put(Telephony.Carriers.MMSC, mmsc)
- values.put(Telephony.Carriers.AUTH_TYPE, authType)
- values.put(Telephony.Carriers.PROTOCOL, convertOptions2Protocol(apnProtocol, context))
- values.put(
- Telephony.Carriers.ROAMING_PROTOCOL,
- convertOptions2Protocol(apnRoaming, context)
- )
- values.put(Telephony.Carriers.TYPE, apnType)
- values.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, networkType)
- values.put(Telephony.Carriers.CARRIER_ENABLED, apnEnable)
- values.put(Telephony.Carriers.EDITED_STATUS, Telephony.Carriers.USER_EDITED)
- if (newApn) {
- val simCarrierId =
- context.getSystemService(TelephonyManager::class.java)!!
- .createForSubscriptionId(subId)
- .getSimCarrierId()
- values.put(Telephony.Carriers.CARRIER_ID, simCarrierId)
- }
+ val contentValueMap = getContentValueMap(context)
+ if (!newApn) contentValueMap.remove(Telephony.Carriers.CARRIER_ID)
+ contentValueMap.forEach { (key, value) -> values.putObject(key, value) }
return values
}
}
data class CustomizedConfig(
- val newApn: Boolean = false,
val readOnlyApn: Boolean = false,
val isAddApnAllowed: Boolean = true,
val readOnlyApnTypes: List = emptyList(),
@@ -227,20 +224,14 @@ fun getApnDataInit(arguments: Bundle, context: Context, uriInit: Uri, subId: Int
*/
fun validateAndSaveApnData(
apnDataInit: ApnData,
- apnData: ApnData,
+ newApnData: ApnData,
context: Context,
- uriInit: Uri,
- networkTypeSelectedOptionsState: SnapshotStateList
-): Boolean {
- // Nothing to do if it's a read only APN
- if (apnData.customizedConfig.readOnlyApn) {
- return true
- }
- val errorMsg = validateApnData(apnData, context)
+ uriInit: Uri
+): String? {
+ val errorMsg = validateApnData(uriInit, newApnData, context)
if (errorMsg != null) {
- return false
+ return errorMsg
}
- val newApnData = apnData.copy(networkType = getNetworkType(networkTypeSelectedOptionsState))
if (newApnData.newApn || (newApnData != apnDataInit)) {
Log.d(TAG, "[validateAndSaveApnData] newApnData.networkType: ${newApnData.networkType}")
updateApnDataToDatabase(
@@ -250,7 +241,7 @@ fun validateAndSaveApnData(
uriInit
)
}
- return true
+ return null
}
/**
@@ -258,7 +249,7 @@ fun validateAndSaveApnData(
*
* @return An error message if the apn data is invalid, otherwise return null.
*/
-fun validateApnData(apnData: ApnData, context: Context): String? {
+fun validateApnData(uri: Uri, apnData: ApnData, context: Context): String? {
var errorMsg: String?
val name = apnData.name
val apn = apnData.apn
@@ -267,11 +258,14 @@ fun validateApnData(apnData: ApnData, context: Context): String? {
} else if (apn == "") {
context.resources.getString(R.string.error_apn_empty)
} else {
- validateMMSC(apnData.validEnabled, apnData.mmsc, context)
+ validateMMSC(true, apnData.mmsc, context)
+ }
+ if (errorMsg == null) {
+ errorMsg = isItemExist(uri, apnData, context)
}
if (errorMsg == null) {
errorMsg = validateAPNType(
- apnData.validEnabled,
+ true,
apnData.apnType,
apnData.customizedConfig.readOnlyApnTypes,
context
diff --git a/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt b/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt
index e4fb1ea47a3..38a84998e07 100644
--- a/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt
@@ -104,7 +104,7 @@ class AppArchiveButton(
userHandle
)
try {
- packageInstaller.requestArchive(app.packageName, pendingIntent.intentSender, 0)
+ packageInstaller.requestArchive(app.packageName, pendingIntent.intentSender)
} catch (e: Exception) {
Log.e(LOG_TAG, "Request archive failed", e)
Toast.makeText(
diff --git a/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt b/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
index a7f971418cd..aafe49382db 100644
--- a/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
+++ b/src/com/android/settings/spa/app/specialaccess/VoiceActivationApps.kt
@@ -56,9 +56,12 @@ class VoiceActivationAppsListModel(context: Context) : AppOpPermissionListModel(
override val appOp = AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
override val permission = Manifest.permission.RECEIVE_SANDBOX_TRIGGER_AUDIO
override val setModeByUid = true
-
+ private var receiveDetectionTrainingDataOpController:AppOpsController? = null
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
super.setAllowed(record, newAllowed)
+ if (!newAllowed && receiveDetectionTrainingDataOpController != null) {
+ receiveDetectionTrainingDataOpController!!.setAllowed(false)
+ }
logPermissionChange(newAllowed)
}
@@ -79,20 +82,21 @@ class VoiceActivationAppsListModel(context: Context) : AppOpPermissionListModel(
isReceiveSandBoxTriggerAudioOpAllowed: () -> Boolean?
): ReceiveDetectionTrainingDataOpSwitchModel {
val context = LocalContext.current
- val ReceiveDetectionTrainingDataOpController = remember {
+ receiveDetectionTrainingDataOpController = remember {
AppOpsController(
context = context,
app = record.app,
op = AppOpsManager.OP_RECEIVE_SANDBOXED_DETECTION_TRAINING_DATA,
)
}
- val isReceiveDetectionTrainingDataOpAllowed = isReceiveDetectionTrainingDataOpAllowed(record, ReceiveDetectionTrainingDataOpController)
+ val isReceiveDetectionTrainingDataOpAllowed = isReceiveDetectionTrainingDataOpAllowed(record, receiveDetectionTrainingDataOpController!!)
+
return remember(record) {
ReceiveDetectionTrainingDataOpSwitchModel(
context,
record,
isReceiveSandBoxTriggerAudioOpAllowed,
- ReceiveDetectionTrainingDataOpController,
+ receiveDetectionTrainingDataOpController!!,
isReceiveDetectionTrainingDataOpAllowed,
)
}.also { model -> LaunchedEffect(model, Dispatchers.Default) { model.initState() } }
diff --git a/src/com/android/settings/system/FactoryResetPreferenceController.java b/src/com/android/settings/system/FactoryResetPreferenceController.java
index e62e548fec0..6d811797188 100644
--- a/src/com/android/settings/system/FactoryResetPreferenceController.java
+++ b/src/com/android/settings/system/FactoryResetPreferenceController.java
@@ -28,6 +28,7 @@ import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.preference.Preference;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.Settings;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.factory_reset.Flags;
@@ -36,11 +37,14 @@ public class FactoryResetPreferenceController extends BasePreferenceController {
private static final String TAG = "FactoryResetPreference";
- private static final String ACTION_PREPARE_FACTORY_RESET =
+ @VisibleForTesting
+ static final String ACTION_PREPARE_FACTORY_RESET =
"com.android.settings.ACTION_PREPARE_FACTORY_RESET";
private final UserManager mUm;
- private ActivityResultLauncher mFactoryResetPreparationLauncher;
+
+ @VisibleForTesting
+ ActivityResultLauncher mFactoryResetPreparationLauncher;
public FactoryResetPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
diff --git a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
index 321fcf58b9f..01d0df9981b 100644
--- a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
@@ -17,75 +17,125 @@ package com.android.settings.system;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
-import com.android.settings.testutils.shadow.ShadowUserManager;
-import com.android.settings.testutils.shadow.ShadowUtils;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.preference.Preference;
+
+import com.google.common.collect.ImmutableList;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowUserManager.class)
public class FactoryResetPreferenceControllerTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final String FACTORY_RESET_KEY = "factory_reset";
+ private static final String FACTORY_RESET_APP_PACKAGE = "com.frw_app";
- private ShadowUserManager mShadowUserManager;
+ @Mock private ActivityResultLauncher mFactoryResetLauncher;
+ @Mock private Preference mPreference;
+ @Mock private Context mContext;
+ @Mock private PackageManager mPackageManager;
+ @Mock private UserManager mUserManager;
+ private ResolveInfo mFactoryResetAppResolveInfo;
+ private PackageInfo mFactoryResetAppPackageInfo;
- private Context mContext;
private FactoryResetPreferenceController mController;
@Before
- public void setUp() {
- mContext = RuntimeEnvironment.application;
- mShadowUserManager = ShadowUserManager.getShadow();
-
+ public void setUp() throws PackageManager.NameNotFoundException {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
mController = new FactoryResetPreferenceController(mContext, FACTORY_RESET_KEY);
+ mFactoryResetAppResolveInfo = new ResolveInfo();
+ mFactoryResetAppResolveInfo.activityInfo = new ActivityInfo();
+ mFactoryResetAppResolveInfo.activityInfo.packageName = FACTORY_RESET_APP_PACKAGE;
+ mFactoryResetAppPackageInfo = new PackageInfo();
+ mFactoryResetAppPackageInfo.requestedPermissions =
+ new String[] {Manifest.permission.PREPARE_FACTORY_RESET};
+ mFactoryResetAppPackageInfo.requestedPermissionsFlags = new int[] {
+ PackageInfo.REQUESTED_PERMISSION_GRANTED
+ };
+ when(mPackageManager.resolveActivity(any(), anyInt()))
+ .thenReturn(mFactoryResetAppResolveInfo);
+ when(mPackageManager.getPackageInfo(anyString(), anyInt()))
+ .thenReturn(mFactoryResetAppPackageInfo);
+ when(mPreference.getKey()).thenReturn(FACTORY_RESET_KEY);
+ mController.mFactoryResetPreparationLauncher = mFactoryResetLauncher;
+
}
@After
public void tearDown() {
- ShadowUtils.reset();
- mShadowUserManager.setIsAdminUser(false);
- mShadowUserManager.setIsDemoUser(false);
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_DEMO_MODE, 0);
+ Mockito.reset(mUserManager, mPackageManager);
+ Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+ Settings.Global.DEVICE_DEMO_MODE, 0);
}
@Ignore("b/314930928")
@Test
public void isAvailable_systemUser() {
- mShadowUserManager.setIsAdminUser(true);
+ when(mUserManager.isAdminUser()).thenReturn(true);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_nonSystemUser() {
- mShadowUserManager.setIsAdminUser(false);
- mShadowUserManager.setIsDemoUser(false);
+ when(mUserManager.isAdminUser()).thenReturn(false);
+ when(mUserManager.isDemoUser()).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_demoUser() {
- mShadowUserManager.setIsAdminUser(false);
+ when(mUserManager.isAdminUser()).thenReturn(false);
// Place the device in demo mode.
- Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_DEMO_MODE, 1);
+ Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
+ Settings.Global.DEVICE_DEMO_MODE, 1);
// Indicate the user is a demo user.
- mShadowUserManager.addUser(UserHandle.myUserId(), "test", UserInfo.FLAG_DEMO);
+ when(mUserManager.getUserProfiles())
+ .thenReturn(ImmutableList.of(new UserHandle(UserHandle.myUserId())));
+ when(mUserManager.getUserInfo(eq(UserHandle.myUserId())))
+ .thenReturn(new UserInfo(UserHandle.myUserId(), "test", UserInfo.FLAG_DEMO));
assertThat(mController.isAvailable()).isFalse();
}
@@ -94,4 +144,16 @@ public class FactoryResetPreferenceControllerTest {
public void getPreferenceKey() {
assertThat(mController.getPreferenceKey()).isEqualTo(FACTORY_RESET_KEY);
}
+
+ @Test
+ @RequiresFlagsEnabled(com.android.settings.factory_reset.Flags.FLAG_ENABLE_FACTORY_RESET_WIZARD)
+ public void handlePreference_factoryResetWizardEnabled() {
+ ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue();
+ verify(mFactoryResetLauncher).launch(intentArgumentCaptor.capture());
+ assertThat(intentArgumentCaptor.getValue()).isNotNull();
+ assertThat(intentArgumentCaptor.getValue().getAction())
+ .isEqualTo(FactoryResetPreferenceController.ACTION_PREPARE_FACTORY_RESET);
+ }
}
diff --git a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowDeviceConfig.java b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowDeviceConfig.java
index dfd09887e54..acb1dd8a9a7 100644
--- a/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowDeviceConfig.java
+++ b/tests/robotests/testutils/com/android/settings/testutils/shadow/ShadowDeviceConfig.java
@@ -16,7 +16,6 @@
package com.android.settings.testutils.shadow;
-import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@@ -25,7 +24,7 @@ import java.util.HashMap;
import java.util.Map;
@Implements(android.provider.DeviceConfig.class)
-public class ShadowDeviceConfig {
+public class ShadowDeviceConfig extends org.robolectric.shadows.ShadowDeviceConfig {
private static Map sPropertyMaps = new HashMap<>();
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt
index 6b4cc0d4a1f..2afb3f1ee62 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt
@@ -136,8 +136,7 @@ class AppArchiveButtonTest {
verify(packageInstaller).requestArchive(
eq(PACKAGE_NAME),
- any(),
- eq(0)
+ any()
)
}