• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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