Snap for 11330025 from b34e67228f to 24Q2-release
Change-Id: Ie93e56816277d813cf1c3a6c7dabf675c1d6146e
This commit is contained in:
@@ -5036,6 +5036,12 @@
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
tools:node="remove" />
|
||||
|
||||
<activity
|
||||
android:name="com.android.settings.network.WepNetworkDialogActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.SpaLib.Dialog">
|
||||
</activity>
|
||||
|
||||
<!-- This is the longest AndroidManifest.xml ever. -->
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -3323,6 +3323,8 @@
|
||||
<string name="menu_cancel">Cancel</string>
|
||||
<!-- APN error dialog title -->
|
||||
<string name="error_title"></string>
|
||||
<!-- APN error dialog messages when the new apn is a duplicate: -->
|
||||
<string name="error_duplicate_apn_entry">Duplicate apn entry.</string>
|
||||
<!-- APN error dialog messages: -->
|
||||
<string name="error_name_empty">The Name field can\u2019t be empty.</string>
|
||||
<!-- APN error dialog messages: -->
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
111
src/com/android/settings/network/WepNetworkDialogActivity.kt
Normal file
111
src/com/android/settings/network/WepNetworkDialogActivity.kt
Normal file
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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<ApnData>, 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<ApnData>, 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()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> = 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
|
||||
}
|
||||
@@ -72,41 +72,38 @@ data class ApnData(
|
||||
val validEnabled: Boolean = false,
|
||||
val customizedConfig: CustomizedConfig = CustomizedConfig()
|
||||
) {
|
||||
fun getContentValueMap(context: Context): MutableMap<String, Any> {
|
||||
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<String> = 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<Int>
|
||||
): 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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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() } }
|
||||
|
||||
@@ -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<Intent> mFactoryResetPreparationLauncher;
|
||||
|
||||
@VisibleForTesting
|
||||
ActivityResultLauncher<Intent> mFactoryResetPreparationLauncher;
|
||||
|
||||
public FactoryResetPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
|
||||
@@ -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<Intent> 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<Intent> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, String> sPropertyMaps = new HashMap<>();
|
||||
|
||||
|
||||
@@ -136,8 +136,7 @@ class AppArchiveButtonTest {
|
||||
|
||||
verify(packageInstaller).requestArchive(
|
||||
eq(PACKAGE_NAME),
|
||||
any(),
|
||||
eq(0)
|
||||
any()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user