1 /*
<lambda>null2  * 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.database.Cursor
22 import android.net.Uri
23 import android.provider.Telephony
24 import android.telephony.SubscriptionManager
25 import android.telephony.TelephonyManager
26 import android.util.Log
27 import com.android.settings.R
28 import com.android.settingslib.utils.ThreadUtils
29 import java.util.Locale
30 
31 val Projection = arrayOf(
32     Telephony.Carriers._ID,  // 0
33     Telephony.Carriers.NAME,  // 1
34     Telephony.Carriers.APN,  // 2
35     Telephony.Carriers.PROXY,  // 3
36     Telephony.Carriers.PORT,  // 4
37     Telephony.Carriers.USER,  // 5
38     Telephony.Carriers.SERVER,  // 6
39     Telephony.Carriers.PASSWORD,  // 7
40     Telephony.Carriers.MMSC,  // 8
41     Telephony.Carriers.MMSPROXY,  // 9
42     Telephony.Carriers.MMSPORT,  // 10
43     Telephony.Carriers.AUTH_TYPE,  // 11
44     Telephony.Carriers.TYPE,  // 12
45     Telephony.Carriers.PROTOCOL,  // 13
46     Telephony.Carriers.CARRIER_ENABLED,  // 14
47     Telephony.Carriers.NETWORK_TYPE_BITMASK, // 15
48     Telephony.Carriers.ROAMING_PROTOCOL,  // 16
49     Telephony.Carriers.EDITED_STATUS,  // 17
50     Telephony.Carriers.USER_EDITABLE,  // 18
51 )
52 
53 private const val TAG = "ApnRepository"
54 
55 /**
56  * Query apn related information based on uri.
57  * @param uri URI data used for query.
58  *
59  * @return Stored apn related information.
60  */
61 fun getApnDataFromUri(uri: Uri, context: Context): ApnData {
62     var apnData = ApnData()
63     val contentResolver = context.contentResolver
64 
65     contentResolver.query(
66         uri,
67         Projection,
68         null /* selection */,
69         null /* selectionArgs */,
70         null /* sortOrder */
71     ).use { cursor ->
72         if (cursor != null && cursor.moveToFirst()) {
73             apnData = ApnData(
74                 id = cursor.getInt(Telephony.Carriers._ID),
75                 name = cursor.getString(Telephony.Carriers.NAME),
76                 apn = cursor.getString(Telephony.Carriers.APN),
77                 proxy = cursor.getString(Telephony.Carriers.PROXY),
78                 port = cursor.getString(Telephony.Carriers.PORT),
79                 userName = cursor.getString(Telephony.Carriers.USER),
80                 passWord = cursor.getString(Telephony.Carriers.PASSWORD),
81                 server = cursor.getString(Telephony.Carriers.SERVER),
82                 mmsc = cursor.getString(Telephony.Carriers.MMSC),
83                 mmsProxy = cursor.getString(Telephony.Carriers.MMSPROXY),
84                 mmsPort = cursor.getString(Telephony.Carriers.MMSPORT),
85                 authType = cursor.getInt(Telephony.Carriers.AUTH_TYPE),
86                 apnType = cursor.getString(Telephony.Carriers.TYPE),
87                 apnProtocol = context.convertProtocol2Options(
88                     cursor.getString(Telephony.Carriers.PROTOCOL)
89                 ),
90                 apnRoaming = context.convertProtocol2Options(
91                     cursor.getString(Telephony.Carriers.ROAMING_PROTOCOL)
92                 ),
93                 apnEnable = cursor.getInt(Telephony.Carriers.CARRIER_ENABLED) == 1,
94                 networkType = cursor.getLong(Telephony.Carriers.NETWORK_TYPE_BITMASK),
95                 edited = cursor.getInt(Telephony.Carriers.EDITED_STATUS),
96                 userEditable = cursor.getInt(Telephony.Carriers.USER_EDITABLE),
97             )
98         }
99     }
100     if (apnData.name == "") {
101         Log.d(TAG, "Can't get apnData from Uri $uri")
102     }
103     return apnData
104 }
105 
getStringnull106 private fun Cursor.getString(columnName: String) = getString(getColumnIndexOrThrow(columnName))
107 private fun Cursor.getInt(columnName: String) = getInt(getColumnIndexOrThrow(columnName))
108 private fun Cursor.getLong(columnName: String) = getLong(getColumnIndexOrThrow(columnName))
109 
110 /**
111  * Returns The UI choice index corresponding to the given raw value of the protocol preference
112  * (e.g., "IPV4V6").
113  * If unknown, return -1.
114  */
115 private fun Context.convertProtocol2Options(protocol: String): Int {
116     var normalizedProtocol = protocol.uppercase(Locale.getDefault())
117     if (normalizedProtocol == "IPV4") normalizedProtocol = "IP"
118     return resources.getStringArray(R.array.apn_protocol_values).indexOf(normalizedProtocol)
119 }
120 
Contextnull121 fun Context.convertOptions2Protocol(protocolIndex: Int): String =
122     resources.getStringArray(R.array.apn_protocol_values).getOrElse(protocolIndex) { "" }
123 
updateApnDataToDatabasenull124 fun updateApnDataToDatabase(
125     newApn: Boolean,
126     values: ContentValues,
127     context: Context,
128     uriInit: Uri
129 ) {
130     ThreadUtils.postOnBackgroundThread {
131         if (newApn) {
132             Log.d(TAG, "Adding an new APN to the database $uriInit $values")
133             val newUri = context.contentResolver.insert(uriInit, values)
134             if (newUri == null) {
135                 Log.e(TAG, "Can't add a new apn to database $uriInit")
136             }
137         } else {
138             Log.d(TAG, "Updating an existing APN to the database $uriInit $values")
139             context.contentResolver.update(
140                 uriInit, values, null /* where */, null /* selection Args */
141             )
142         }
143     }
144 }
145 
146 /** Not allowing add duplicated items, if the values of the following keys are all identical. */
147 private val NonDuplicatedKeys = setOf(
148     Telephony.Carriers.APN,
149     Telephony.Carriers.PROXY,
150     Telephony.Carriers.PORT,
151     Telephony.Carriers.MMSC,
152     Telephony.Carriers.MMSPROXY,
153     Telephony.Carriers.MMSPORT,
154     Telephony.Carriers.PROTOCOL,
155     Telephony.Carriers.ROAMING_PROTOCOL,
156 )
157 
isItemExistnull158 fun isItemExist(apnData: ApnData, context: Context): String? {
159     val selectionMap = apnData.getContentValueMap(context).filterKeys { it in NonDuplicatedKeys }
160         .mapKeys { "${it.key} = ?" }
161         .toMutableMap()
162     if (apnData.id != -1) selectionMap += "${Telephony.Carriers._ID} != ?" to apnData.id
163     val list = selectionMap.entries.toList()
164     val selection = list.joinToString(" AND ") { it.key }
165     val selectionArgs: Array<String> = list.map { it.value.toString() }.toTypedArray()
166     context.contentResolver.query(
167         Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, apnData.subId.toString()),
168         /* projection = */ emptyArray(),
169         selection,
170         selectionArgs,
171         /* sortOrder = */ null,
172     )?.use { cursor ->
173         if (cursor.count > 0) {
174             return context.resources.getString(R.string.error_duplicate_apn_entry)
175         }
176     }
177     return null
178 }
179 
Contextnull180 fun Context.getApnIdMap(subId: Int): Map<String, Any> {
181     val subInfo = getSystemService(SubscriptionManager::class.java)!!
182         .getActiveSubscriptionInfo(subId)
183     val carrierId = subInfo.carrierId
184     return if (carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) {
185         mapOf(Telephony.Carriers.CARRIER_ID to carrierId)
186     } else {
187         mapOf(Telephony.Carriers.NUMERIC to subInfo.mccString + subInfo.mncString)
188     }.also { Log.d(TAG, "[$subId] New APN item with id: $it") }
189 }
190