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 import com.android.settings.network.apn.ApnTypes.getReadOnlyApnTypes
29
30 private const val TAG = "ApnStatus"
31
32 data class ApnData(
33 val id: Int = -1,
34 val name: String = "",
35 val apn: String = "",
36 val proxy: String = "",
37 val port: String = "",
38 val userName: String = "",
39 val passWord: String = "",
40 val server: String = "",
41 val mmsc: String = "",
42 val mmsProxy: String = "",
43 val mmsPort: String = "",
44 val authType: Int = -1,
45 val apnType: String = "",
46 val apnProtocol: Int = -1,
47 val apnRoaming: Int = -1,
48 val carrierEnabled: Boolean = true,
49 val networkType: Long = 0,
50 val edited: Int = Telephony.Carriers.USER_EDITED,
51 val userEditable: Int = 1,
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 carrierEnabled,
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 // TODO: mIsCarrierIdApn
138 return disableInit(apnDataInit)
139 }
140
141 /**
142 * Validates the apn data and save it to the database if it's valid.
143 * A dialog with error message will be displayed if the APN data is invalid.
144 *
145 * @return true if there is no error
146 */
validateAndSaveApnDatanull147 fun validateAndSaveApnData(
148 apnDataInit: ApnData,
149 newApnData: ApnData,
150 context: Context,
151 uriInit: Uri
152 ): String? {
153 val errorMsg = validateApnData(newApnData, context)
154 if (errorMsg != null) {
155 return errorMsg
156 }
157 if (newApnData.newApn || (newApnData != apnDataInit)) {
158 Log.d(TAG, "[validateAndSaveApnData] newApnData.networkType: ${newApnData.networkType}")
159 updateApnDataToDatabase(
160 newApnData.newApn,
161 newApnData.getContentValues(context),
162 context,
163 uriInit
164 )
165 }
166 return null
167 }
168
169 /**
170 * Validates whether the apn data is valid.
171 *
172 * @return An error message if the apn data is invalid, otherwise return null.
173 */
validateApnDatanull174 fun validateApnData(apnData: ApnData, context: Context): String? {
175 val errorMsg: String? = when {
176 apnData.name.isEmpty() -> context.resources.getString(R.string.error_name_empty)
177 apnData.apn.isEmpty() -> context.resources.getString(R.string.error_apn_empty)
178 apnData.apnType.isEmpty() -> context.resources.getString(R.string.error_apn_type_empty)
179 else -> validateMMSC(true, apnData.mmsc, context) ?: isItemExist(apnData, context)
180 }
181 return errorMsg?.also { Log.d(TAG, "APN data not valid, reason: $it") }
182 }
183
184 /**
185 * Initialize CustomizedConfig information through subId.
186 * @param subId subId information obtained from arguments.
187 *
188 * @return Initialized CustomizedConfig information.
189 */
getCarrierCustomizedConfignull190 fun getCarrierCustomizedConfig(
191 apnInit: ApnData,
192 configManager: CarrierConfigManager
193 ): CustomizedConfig {
194 fun log(message: String) {
195 Log.d(TAG, "getCarrierCustomizedConfig: $message")
196 }
197
198 val b = configManager.getConfigForSubId(
199 apnInit.subId,
200 CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY,
201 CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY,
202 CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY,
203 CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING,
204 CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING,
205 CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL
206 )
207 val customizedConfig = CustomizedConfig(
208 readOnlyApnTypes = b.getReadOnlyApnTypes(),
209 readOnlyApnFields = b.getStringArray(
210 CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY
211 )?.toList() ?: emptyList(),
212 defaultApnTypes = b.getStringArray(
213 CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY
214 )?.toList(),
215 defaultApnProtocol = b.getString(
216 CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING
217 ) ?: "",
218 defaultApnRoamingProtocol = b.getString(
219 CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING
220 ) ?: "",
221 isAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL),
222 )
223 if (customizedConfig.readOnlyApnTypes.isNotEmpty()) {
224 log("read only APN type: " + customizedConfig.readOnlyApnTypes)
225 }
226 customizedConfig.defaultApnTypes?.takeIf { it.isNotEmpty() }?.let {
227 log("default apn types: $it")
228 }
229 if (customizedConfig.defaultApnProtocol.isNotEmpty()) {
230 log("default apn protocol: ${customizedConfig.defaultApnProtocol}")
231 }
232 if (customizedConfig.defaultApnRoamingProtocol.isNotEmpty()) {
233 log("default apn roaming protocol: ${customizedConfig.defaultApnRoamingProtocol}")
234 }
235 if (!customizedConfig.isAddApnAllowed) {
236 log("not allow to add new APN")
237 }
238 return customizedConfig
239 }
240
isReadOnlynull241 private fun ApnData.isReadOnly(): Boolean {
242 Log.d(TAG, "isReadOnly: edited $edited")
243 if (edited == Telephony.Carriers.USER_EDITED) return false
244 // if it's not a USER_EDITED apn, check if it's read-only
245 return userEditable == 0 ||
246 ApnTypes.isApnTypeReadOnly(apnType, customizedConfig.readOnlyApnTypes)
247 }
248
disableInitnull249 fun disableInit(apnDataInit: ApnData): ApnData {
250 if (apnDataInit.isReadOnly()) {
251 Log.d(TAG, "disableInit: read-only APN")
252 return apnDataInit.copy(
253 customizedConfig = apnDataInit.customizedConfig.copy(readOnlyApn = true)
254 )
255 }
256 val readOnlyApnFields = apnDataInit.customizedConfig.readOnlyApnFields
257 if (readOnlyApnFields.isNotEmpty()) {
258 Log.d(TAG, "disableInit: readOnlyApnFields $readOnlyApnFields)")
259 }
260 return apnDataInit
261 }
262
deleteApnnull263 fun deleteApn(uri: Uri, context: Context) {
264 val contentResolver = context.contentResolver
265 contentResolver.delete(uri, null, null)
266 }
267
validateMMSCnull268 fun validateMMSC(validEnabled: Boolean, mmsc: String, context: Context): String? {
269 return if (validEnabled && mmsc != "" && !mmsc.matches(Regex("^https?:\\/\\/.+")))
270 context.resources.getString(R.string.error_mmsc_valid)
271 else null
272 }
273
validateNamenull274 fun validateName(validEnabled: Boolean, name: String, context: Context): String? {
275 return if (validEnabled && (name == "")) context.resources.getString(R.string.error_name_empty)
276 else null
277 }
278
validateAPNnull279 fun validateAPN(validEnabled: Boolean, apn: String, context: Context): String? {
280 return if (validEnabled && (apn == "")) context.resources.getString(R.string.error_apn_empty)
281 else null
282 }
283