1 /*
2 * Copyright (C) 2023 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.ContentValues
20 import android.content.Context
21 import android.net.Uri
22 import android.os.Bundle
23 import android.provider.Telephony
24 import android.telephony.CarrierConfigManager
25 import android.util.Log
26 import com.android.settings.R
27 import com.android.settings.network.apn.ApnTypes.getPreSelectedApnType
28
29 private const val TAG = "ApnStatus"
30
31 data class ApnData(
32 val id: Int = -1,
33 val name: String = "",
34 val apn: String = "",
35 val proxy: String = "",
36 val port: String = "",
37 val userName: String = "",
38 val passWord: String = "",
39 val server: String = "",
40 val mmsc: String = "",
41 val mmsProxy: String = "",
42 val mmsPort: String = "",
43 val authType: Int = -1,
44 val apnType: String = "",
45 val apnProtocol: Int = -1,
46 val apnRoaming: Int = -1,
47 val apnEnable: Boolean = true,
48 val networkType: Long = 0,
49 val edited: Int = Telephony.Carriers.USER_EDITED,
50 val userEditable: Int = 1,
51 val apnEnableEnabled: Boolean = true,
52 val newApn: Boolean = false,
53 val subId: Int = -1,
54 val validEnabled: Boolean = false,
55 val customizedConfig: CustomizedConfig = CustomizedConfig()
56 ) {
getContentValueMapnull57 fun getContentValueMap(context: Context): Map<String, Any> = mapOf(
58 Telephony.Carriers.NAME to name,
59 Telephony.Carriers.APN to apn,
60 Telephony.Carriers.PROXY to proxy,
61 Telephony.Carriers.PORT to port,
62 Telephony.Carriers.USER to userName,
63 Telephony.Carriers.SERVER to server,
64 Telephony.Carriers.PASSWORD to passWord,
65 Telephony.Carriers.MMSC to mmsc,
66 Telephony.Carriers.MMSPROXY to mmsProxy,
67 Telephony.Carriers.MMSPORT to mmsPort,
68 Telephony.Carriers.AUTH_TYPE to authType,
69 Telephony.Carriers.PROTOCOL to context.convertOptions2Protocol(apnProtocol),
70 Telephony.Carriers.ROAMING_PROTOCOL to context.convertOptions2Protocol(apnRoaming),
71 Telephony.Carriers.TYPE to apnType,
72 Telephony.Carriers.NETWORK_TYPE_BITMASK to networkType,
73 // Copy network type into lingering network type.
74 Telephony.Carriers.LINGERING_NETWORK_TYPE_BITMASK to networkType,
75 Telephony.Carriers.CARRIER_ENABLED to apnEnable,
76 Telephony.Carriers.EDITED_STATUS to Telephony.Carriers.USER_EDITED,
77 )
78
79 fun getContentValues(context: Context) = ContentValues().apply {
80 if (newApn) context.getApnIdMap(subId).forEach(::putObject)
81 getContentValueMap(context).forEach(::putObject)
82 }
83
isFieldEnablednull84 fun isFieldEnabled(vararg fieldName: String): Boolean =
85 !customizedConfig.readOnlyApn &&
86 fieldName.all { it !in customizedConfig.readOnlyApnFields }
87 }
88
89 data class CustomizedConfig(
90 val readOnlyApn: Boolean = false,
91 val isAddApnAllowed: Boolean = true,
92 val readOnlyApnTypes: List<String> = emptyList(),
93 val readOnlyApnFields: List<String> = emptyList(),
94 val defaultApnTypes: List<String>? = null,
95 val defaultApnProtocol: String = "",
96 val defaultApnRoamingProtocol: String = "",
97 )
98
99 /**
100 * Initialize ApnData according to the arguments.
101 * @param arguments The data passed in when the user calls PageProvider.
102 * @param uriInit The decoded user incoming uri data in Page.
103 * @param subId The subId obtained in arguments.
104 *
105 * @return Initialized CustomizedConfig information.
106 */
getApnDataInitnull107 fun getApnDataInit(arguments: Bundle, context: Context, uriInit: Uri, subId: Int): ApnData? {
108 val uriType = arguments.getString(URI_TYPE) ?: return null
109
110 if (!uriInit.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
111 Log.e(TAG, "Insert request not for carrier table. Uri: $uriInit")
112 return null
113 }
114
115 var apnDataInit = when (uriType) {
116 EDIT_URL -> getApnDataFromUri(uriInit, context)
117 INSERT_URL -> ApnData()
118 else -> return null
119 }
120
121 if (uriType == INSERT_URL) {
122 apnDataInit = apnDataInit.copy(newApn = true)
123 }
124
125 apnDataInit = apnDataInit.copy(subId = subId)
126 val configManager =
127 context.getSystemService(Context.CARRIER_CONFIG_SERVICE) as CarrierConfigManager
128 apnDataInit =
129 apnDataInit.copy(customizedConfig = getCarrierCustomizedConfig(apnDataInit, configManager))
130
131 if (apnDataInit.newApn) {
132 apnDataInit = apnDataInit.copy(
133 apnType = getPreSelectedApnType(apnDataInit.customizedConfig)
134 )
135 }
136
137 apnDataInit = apnDataInit.copy(
138 apnEnableEnabled =
139 context.resources.getBoolean(R.bool.config_allow_edit_carrier_enabled)
140 )
141 // TODO: mIsCarrierIdApn
142 return disableInit(apnDataInit)
143 }
144
145 /**
146 * Validates the apn data and save it to the database if it's valid.
147 * A dialog with error message will be displayed if the APN data is invalid.
148 *
149 * @return true if there is no error
150 */
validateAndSaveApnDatanull151 fun validateAndSaveApnData(
152 apnDataInit: ApnData,
153 newApnData: ApnData,
154 context: Context,
155 uriInit: Uri
156 ): String? {
157 val errorMsg = validateApnData(newApnData, context)
158 if (errorMsg != null) {
159 return errorMsg
160 }
161 if (newApnData.newApn || (newApnData != apnDataInit)) {
162 Log.d(TAG, "[validateAndSaveApnData] newApnData.networkType: ${newApnData.networkType}")
163 updateApnDataToDatabase(
164 newApnData.newApn,
165 newApnData.getContentValues(context),
166 context,
167 uriInit
168 )
169 }
170 return null
171 }
172
173 /**
174 * Validates whether the apn data is valid.
175 *
176 * @return An error message if the apn data is invalid, otherwise return null.
177 */
validateApnDatanull178 fun validateApnData(apnData: ApnData, context: Context): String? {
179 val errorMsg: String? = when {
180 apnData.name.isEmpty() -> context.resources.getString(R.string.error_name_empty)
181 apnData.apn.isEmpty() -> context.resources.getString(R.string.error_apn_empty)
182 apnData.apnType.isEmpty() -> context.resources.getString(R.string.error_apn_type_empty)
183 else -> validateMMSC(true, apnData.mmsc, context) ?: isItemExist(apnData, context)
184 }
185 return errorMsg?.also { Log.d(TAG, "APN data not valid, reason: $it") }
186 }
187
188 /**
189 * Initialize CustomizedConfig information through subId.
190 * @param subId subId information obtained from arguments.
191 *
192 * @return Initialized CustomizedConfig information.
193 */
getCarrierCustomizedConfignull194 fun getCarrierCustomizedConfig(
195 apnInit: ApnData,
196 configManager: CarrierConfigManager
197 ): CustomizedConfig {
198 fun log(message: String) {
199 Log.d(TAG, "getCarrierCustomizedConfig: $message")
200 }
201
202 val b = configManager.getConfigForSubId(
203 apnInit.subId,
204 CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY,
205 CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY,
206 CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY,
207 CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING,
208 CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING,
209 CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL
210 )
211 val customizedConfig = CustomizedConfig(
212 readOnlyApnTypes = b.getStringArray(
213 CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY
214 )?.toList() ?: emptyList(),
215 readOnlyApnFields = b.getStringArray(
216 CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY
217 )?.toList() ?: emptyList(),
218 defaultApnTypes = b.getStringArray(
219 CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY
220 )?.toList(),
221 defaultApnProtocol = b.getString(
222 CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING
223 ) ?: "",
224 defaultApnRoamingProtocol = b.getString(
225 CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING
226 ) ?: "",
227 isAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL),
228 )
229 if (customizedConfig.readOnlyApnTypes.isNotEmpty()) {
230 log("read only APN type: " + customizedConfig.readOnlyApnTypes)
231 }
232 customizedConfig.defaultApnTypes?.takeIf { it.isNotEmpty() }?.let {
233 log("default apn types: $it")
234 }
235 if (customizedConfig.defaultApnProtocol.isNotEmpty()) {
236 log("default apn protocol: ${customizedConfig.defaultApnProtocol}")
237 }
238 if (customizedConfig.defaultApnRoamingProtocol.isNotEmpty()) {
239 log("default apn roaming protocol: ${customizedConfig.defaultApnRoamingProtocol}")
240 }
241 if (!customizedConfig.isAddApnAllowed) {
242 log("not allow to add new APN")
243 }
244 return customizedConfig
245 }
246
isReadOnlynull247 private fun ApnData.isReadOnly(): Boolean {
248 Log.d(TAG, "isReadOnly: edited $edited")
249 if (edited == Telephony.Carriers.USER_EDITED) return false
250 // if it's not a USER_EDITED apn, check if it's read-only
251 return userEditable == 0 ||
252 ApnTypes.isApnTypeReadOnly(apnType, customizedConfig.readOnlyApnTypes)
253 }
254
disableInitnull255 fun disableInit(apnDataInit: ApnData): ApnData {
256 if (apnDataInit.isReadOnly()) {
257 Log.d(TAG, "disableInit: read-only APN")
258 return apnDataInit.copy(
259 customizedConfig = apnDataInit.customizedConfig.copy(readOnlyApn = true)
260 )
261 }
262 val readOnlyApnFields = apnDataInit.customizedConfig.readOnlyApnFields
263 if (readOnlyApnFields.isNotEmpty()) {
264 Log.d(TAG, "disableInit: readOnlyApnFields $readOnlyApnFields)")
265 }
266 return apnDataInit
267 }
268
deleteApnnull269 fun deleteApn(uri: Uri, context: Context) {
270 val contentResolver = context.contentResolver
271 contentResolver.delete(uri, null, null)
272 }
273
validateMMSCnull274 fun validateMMSC(validEnabled: Boolean, mmsc: String, context: Context): String? {
275 return if (validEnabled && mmsc != "" && !mmsc.matches(Regex("^https?:\\/\\/.+")))
276 context.resources.getString(R.string.error_mmsc_valid)
277 else null
278 }
279
validateNamenull280 fun validateName(validEnabled: Boolean, name: String, context: Context): String? {
281 return if (validEnabled && (name == "")) context.resources.getString(R.string.error_name_empty)
282 else null
283 }
284
validateAPNnull285 fun validateAPN(validEnabled: Boolean, apn: String, context: Context): String? {
286 return if (validEnabled && (apn == "")) context.resources.getString(R.string.error_apn_empty)
287 else null
288 }
289