1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.network.apn 18 19 import android.content.Context 20 import android.os.PersistableBundle 21 import android.telephony.CarrierConfigManager 22 import android.telephony.data.ApnSetting 23 import android.util.Log 24 import android.widget.Toast 25 import androidx.compose.runtime.mutableStateOf 26 import androidx.compose.ui.text.intl.Locale 27 import androidx.compose.ui.text.toLowerCase 28 import com.android.settings.R 29 import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption 30 31 object ApnTypes { 32 private const val TAG = "ApnTypes" 33 34 private val APN_TYPES = arrayOf( 35 ApnSetting.TYPE_DEFAULT_STRING, 36 ApnSetting.TYPE_MMS_STRING, 37 ApnSetting.TYPE_SUPL_STRING, 38 ApnSetting.TYPE_DUN_STRING, 39 ApnSetting.TYPE_HIPRI_STRING, 40 ApnSetting.TYPE_FOTA_STRING, 41 ApnSetting.TYPE_IMS_STRING, 42 ApnSetting.TYPE_CBS_STRING, 43 ApnSetting.TYPE_IA_STRING, 44 ApnSetting.TYPE_EMERGENCY_STRING, 45 ApnSetting.TYPE_MCX_STRING, 46 ApnSetting.TYPE_XCAP_STRING, 47 ApnSetting.TYPE_VSIM_STRING, 48 ApnSetting.TYPE_BIP_STRING, 49 ApnSetting.TYPE_ENTERPRISE_STRING, 50 ApnSetting.TYPE_OEM_PAID_STRING, 51 ApnSetting.TYPE_OEM_PRIVATE_STRING, 52 ) 53 splitToListnull54 private fun splitToList(apnType: String): List<String> { 55 val types = apnType.split(',').map { it.trim().toLowerCase(Locale.current) } 56 if (hasAllApnTypes(types)) return listOf(ApnSetting.TYPE_ALL_STRING) 57 return APN_TYPES.filter { it in types } 58 } 59 isApnTypeReadOnlynull60 fun isApnTypeReadOnly(apnType: String, readOnlyTypes: List<String>): Boolean { 61 val apnTypes = splitToList(apnType) 62 return ApnSetting.TYPE_ALL_STRING in readOnlyTypes || 63 ApnSetting.TYPE_ALL_STRING in apnTypes && readOnlyTypes.isNotEmpty() || 64 apnTypes.any { it in readOnlyTypes } 65 } 66 <lambda>null67 fun getOptions(context: Context, apnType: String, readOnlyTypes: List<String>) = buildList { 68 val apnTypes = splitToList(apnType) 69 add( 70 context.createSettingsDropdownCheckOption( 71 text = ApnSetting.TYPE_ALL_STRING, 72 isSelectAll = true, 73 changeable = readOnlyTypes.isEmpty(), 74 selected = ApnSetting.TYPE_ALL_STRING in apnTypes, 75 ) 76 ) 77 for (type in APN_TYPES) { 78 add( 79 context.createSettingsDropdownCheckOption( 80 text = type, 81 changeable = ApnSetting.TYPE_ALL_STRING !in readOnlyTypes && 82 type !in readOnlyTypes, 83 selected = ApnSetting.TYPE_ALL_STRING in apnTypes || type in apnTypes, 84 ) 85 ) 86 } 87 }.also { Log.d(TAG, "APN Type options: $it") } 88 Contextnull89 private fun Context.createSettingsDropdownCheckOption( 90 text: String, 91 isSelectAll: Boolean = false, 92 changeable: Boolean, 93 selected: Boolean, 94 ) = SettingsDropdownCheckOption( 95 text = text, 96 isSelectAll = isSelectAll, 97 changeable = changeable, 98 selected = mutableStateOf(selected), 99 ) { 100 if (!changeable) { 101 val message = resources.getString(R.string.error_adding_apn_type, text) 102 Toast.makeText(this, message, Toast.LENGTH_SHORT).show() 103 } 104 } 105 <lambda>null106 fun List<SettingsDropdownCheckOption>.isValid(): Boolean = any { it.selected.value } 107 toApnTypenull108 fun List<SettingsDropdownCheckOption>.toApnType(): String { 109 val (selectAllOptions, regularOptions) = partition { it.isSelectAll } 110 for (selectAllOption in selectAllOptions) { 111 if (selectAllOption.selected.value) return ApnSetting.TYPE_ALL_STRING 112 } 113 return regularOptions.filter { it.selected.value }.joinToString(",") { it.text } 114 } 115 116 private val PreSelectedTypes = setOf( 117 ApnSetting.TYPE_DEFAULT_STRING, 118 ApnSetting.TYPE_MMS_STRING, 119 ApnSetting.TYPE_SUPL_STRING, 120 ApnSetting.TYPE_DUN_STRING, 121 ApnSetting.TYPE_HIPRI_STRING, 122 ApnSetting.TYPE_FOTA_STRING, 123 ApnSetting.TYPE_CBS_STRING, 124 ApnSetting.TYPE_XCAP_STRING, 125 ) 126 getPreSelectedApnTypenull127 fun getPreSelectedApnType(customizedConfig: CustomizedConfig): String = 128 (customizedConfig.defaultApnTypes 129 ?: defaultPreSelectedApnTypes(customizedConfig.readOnlyApnTypes)) 130 .joinToString(",") 131 132 private fun defaultPreSelectedApnTypes(readOnlyApnTypes: List<String>) = 133 if (ApnSetting.TYPE_ALL_STRING in readOnlyApnTypes) emptyList() 134 else PreSelectedTypes.filterNot { it in readOnlyApnTypes } 135 136 /** Array of APN types that are never user-editable */ 137 private val ALWAYS_READ_ONLY_APN_TYPES = 138 arrayOf(ApnSetting.TYPE_OEM_PAID_STRING, ApnSetting.TYPE_OEM_PRIVATE_STRING) 139 140 /** 141 * Fetch complete list of read only APN types. 142 * 143 * The list primarily comes from carrier config, but is also supplied by APN types which are 144 * always read only. 145 */ 146 @JvmStatic PersistableBundlenull147 fun PersistableBundle.getReadOnlyApnTypes(): List<String> { 148 val carrierReadOnlyApnTypes = 149 getStringArray(CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY)?.toList() 150 ?: emptyList() 151 return carrierReadOnlyApnTypes + ALWAYS_READ_ONLY_APN_TYPES 152 } 153 154 /** 155 * Check if passed in array of APN types indicates all APN types 156 * 157 * @param apnTypes array of APN types. "*" indicates all types. 158 * @return true if all apn types are included in the array, false otherwise 159 */ 160 @JvmStatic hasAllApnTypesnull161 fun hasAllApnTypes(apnTypes: List<String>): Boolean = 162 ApnSetting.TYPE_ALL_STRING in apnTypes || APN_TYPES.all { it in apnTypes } 163 } 164