Merge "Create AppDataUsageAppSettingsController" into main
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
index 7ffa0c9..820a9d5 100644
--- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -50,6 +50,7 @@
const val MVNO_TYPE = "mvnoType"
const val MVNO_MATCH_DATA = "mvnoMatchData"
const val EDIT_URL = "editUrl"
+const val INSERT_URL = "insertUrl"
object ApnEditPageProvider : SettingsPageProvider {
@@ -66,7 +67,10 @@
@Composable
override fun Page(arguments: Bundle?) {
- val apnDataInit = ApnData()
+ val uriString = arguments!!.getString(URI)
+ val uriInit = Uri.parse(String(Base64.getDecoder().decode(uriString)))
+ val subId = arguments.getInt(SUB_ID)
+ val apnDataInit = getApnDataInit(arguments, LocalContext.current, uriInit, subId)
val apnDataCur = remember {
mutableStateOf(apnDataInit)
}
diff --git a/src/com/android/settings/network/apn/ApnRepository.kt b/src/com/android/settings/network/apn/ApnRepository.kt
new file mode 100644
index 0000000..3ce7567
--- /dev/null
+++ b/src/com/android/settings/network/apn/ApnRepository.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.apn
+
+import android.content.Context
+import android.net.Uri
+import android.provider.Telephony
+import android.util.Log
+import com.android.settings.R
+import java.util.Locale
+
+const val NAME_INDEX = 1
+const val APN_INDEX = 2
+const val PROXY_INDEX = 3
+const val PORT_INDEX = 4
+const val USER_INDEX = 5
+const val SERVER_INDEX = 6
+const val PASSWORD_INDEX = 7
+const val MMSC_INDEX = 8
+const val MCC_INDEX = 9
+const val MNC_INDEX = 10
+const val MMSPROXY_INDEX = 12
+const val MMSPORT_INDEX = 13
+const val AUTH_TYPE_INDEX = 14
+const val TYPE_INDEX = 15
+const val PROTOCOL_INDEX = 16
+const val CARRIER_ENABLED_INDEX = 17
+const val NETWORK_TYPE_INDEX = 18
+const val ROAMING_PROTOCOL_INDEX = 19
+const val MVNO_TYPE_INDEX = 20
+const val MVNO_MATCH_DATA_INDEX = 21
+const val EDITED_INDEX = 22
+const val USER_EDITABLE_INDEX = 23
+const val CARRIER_ID_INDEX = 24
+
+val sProjection = arrayOf(
+ Telephony.Carriers._ID, // 0
+ Telephony.Carriers.NAME, // 1
+ Telephony.Carriers.APN, // 2
+ Telephony.Carriers.PROXY, // 3
+ Telephony.Carriers.PORT, // 4
+ Telephony.Carriers.USER, // 5
+ Telephony.Carriers.SERVER, // 6
+ Telephony.Carriers.PASSWORD, // 7
+ Telephony.Carriers.MMSC, // 8
+ Telephony.Carriers.MCC, // 9
+ Telephony.Carriers.MNC, // 10
+ Telephony.Carriers.NUMERIC, // 11
+ Telephony.Carriers.MMSPROXY, // 12
+ Telephony.Carriers.MMSPORT, // 13
+ Telephony.Carriers.AUTH_TYPE, // 14
+ Telephony.Carriers.TYPE, // 15
+ Telephony.Carriers.PROTOCOL, // 16
+ Telephony.Carriers.CARRIER_ENABLED, // 17
+ Telephony.Carriers.NETWORK_TYPE_BITMASK, // 18
+ Telephony.Carriers.ROAMING_PROTOCOL, // 19
+ Telephony.Carriers.MVNO_TYPE, // 20
+ Telephony.Carriers.MVNO_MATCH_DATA, // 21
+ Telephony.Carriers.EDITED_STATUS, // 22
+ Telephony.Carriers.USER_EDITABLE, // 23
+ Telephony.Carriers.CARRIER_ID // 24
+)
+
+const val TAG = "ApnRepository"
+
+/**
+ * Query apn related information based on uri.
+ * @param uri URI data used for query.
+ *
+ * @return Stored apn related information.
+ */
+fun getApnDataFromUri(uri: Uri, context: Context): ApnData {
+ var apnData = ApnData()
+ val contentResolver = context.contentResolver
+ val apnProtocolOptions = context.resources.getStringArray(R.array.apn_protocol_entries).toList()
+ val mvnoTypeOptions = context.resources.getStringArray(R.array.mvno_type_entries).toList()
+
+ contentResolver.query(
+ uri,
+ sProjection,
+ null /* selection */,
+ null /* selectionArgs */,
+ null /* sortOrder */
+ ).use { cursor ->
+ if (cursor != null && cursor.moveToFirst()) {
+ val name = cursor.getString(NAME_INDEX)
+ val apn = cursor.getString(APN_INDEX)
+ val proxy = cursor.getString(PROXY_INDEX)
+ val port = cursor.getString(PORT_INDEX)
+ val userName = cursor.getString(USER_INDEX)
+ val server = cursor.getString(SERVER_INDEX)
+ val passWord = cursor.getString(PASSWORD_INDEX)
+ val mmsc = cursor.getString(MMSC_INDEX)
+ val mcc = cursor.getString(MCC_INDEX)
+ val mnc = cursor.getString(MNC_INDEX)
+ val mmsProxy = cursor.getString(MMSPROXY_INDEX)
+ val mmsPort = cursor.getString(MMSPORT_INDEX)
+ val authType = cursor.getInt(AUTH_TYPE_INDEX)
+ val apnType = cursor.getString(TYPE_INDEX)
+ val apnProtocol = convertProtocol2Options(cursor.getString(PROTOCOL_INDEX), context)
+ val apnRoaming =
+ convertProtocol2Options(cursor.getString(ROAMING_PROTOCOL_INDEX), context)
+ val apnEnable = cursor.getInt(CARRIER_ENABLED_INDEX) == 1
+ val networkType = cursor.getLong(NETWORK_TYPE_INDEX)
+ val mvnoType = cursor.getString(MVNO_TYPE_INDEX)
+ val mvnoValue = cursor.getString(MVNO_MATCH_DATA_INDEX)
+
+ val edited = cursor.getInt(EDITED_INDEX)
+ val userEditable = cursor.getInt(USER_EDITABLE_INDEX)
+ val carrierId = cursor.getInt(CARRIER_ID_INDEX)
+
+ apnData = apnData.copy(
+ name = name,
+ apn = apn,
+ proxy = proxy,
+ port = port,
+ userName = userName,
+ passWord = passWord,
+ server = server,
+ mmsc = mmsc,
+ mmsProxy = mmsProxy,
+ mmsPort = mmsPort,
+ mcc = mcc,
+ mnc = mnc,
+ authType = authType,
+ apnType = apnType,
+ apnProtocol = apnProtocolOptions.indexOf(apnProtocol),
+ apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
+ apnEnable = apnEnable,
+ networkType = networkType,
+ mvnoType = mvnoTypeOptions.indexOf(mvnoType),
+ mvnoValue = mvnoValue,
+ edited = edited,
+ userEditable = userEditable,
+ carrierId = carrierId
+ )
+ }
+ }
+ if (apnData.name == "") {
+ Log.d(TAG, "Can't get apnData from Uri $uri")
+ }
+ return apnData
+}
+
+/**
+ * Returns The UI choice (e.g., "IPv4/IPv6") corresponding to the given
+ * raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
+ * return null.
+ *
+ * @return UI choice
+ */
+private fun convertProtocol2Options(raw: String, context: Context): String {
+ val apnProtocolOptions = context.resources.getStringArray(R.array.apn_protocol_entries).toList()
+ val apnProtocolValues = context.resources.getStringArray(R.array.apn_protocol_values).toList()
+
+ var uRaw = raw.uppercase(Locale.getDefault())
+ uRaw = if (uRaw == "IPV4") "IP" else uRaw
+ val protocolIndex = apnProtocolValues.indexOf(uRaw)
+ return if (protocolIndex == -1) {
+ ""
+ } else {
+ try {
+ apnProtocolOptions[protocolIndex]
+ } catch (e: ArrayIndexOutOfBoundsException) {
+ ""
+ }
+ }
+}
diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt
index c8d1b83..ac9f28b 100644
--- a/src/com/android/settings/network/apn/ApnStatus.kt
+++ b/src/com/android/settings/network/apn/ApnStatus.kt
@@ -16,8 +16,16 @@
package com.android.settings.network.apn
+import android.content.Context
+import android.net.Uri
+import android.os.Bundle
import android.provider.Telephony
+import android.telephony.CarrierConfigManager
import android.telephony.TelephonyManager
+import android.text.TextUtils
+import android.util.Log
+import com.android.internal.util.ArrayUtils
+import com.android.settings.R
data class ApnData(
val name: String = "",
@@ -42,26 +50,147 @@
var mvnoValue: String = "",
val edited: Int = Telephony.Carriers.USER_EDITED,
val userEditable: Int = 1,
- val carrierId: Int = TelephonyManager.UNKNOWN_CARRIER_ID
-) {
- var nameEnabled = true
- var apnEnabled = true
- var proxyEnabled = true
- var portEnabled = true
- var userNameEnabled = true
- var passWordEnabled = true
- var serverEnabled = true
- var mmscEnabled = true
- var mmsProxyEnabled = true
- var mmsPortEnabled = true
- var mccEnabled = true
- var mncEnabled = true
- var authTypeEnabled = true
- var apnTypeEnabled = true
- var apnProtocolEnabled = true
- var apnRoamingEnabled = true
- var apnEnableEnabled = true
- var networkTypeEnabled = true
- var mvnoTypeEnabled = true
- var mvnoValueEnabled = false
+ val carrierId: Int = TelephonyManager.UNKNOWN_CARRIER_ID,
+ val nameEnabled: Boolean = true,
+ val apnEnabled: Boolean = true,
+ val proxyEnabled: Boolean = true,
+ val portEnabled: Boolean = true,
+ val userNameEnabled: Boolean = true,
+ val passWordEnabled: Boolean = true,
+ val serverEnabled: Boolean = true,
+ val mmscEnabled: Boolean = true,
+ val mmsProxyEnabled: Boolean = true,
+ val mmsPortEnabled: Boolean = true,
+ val mccEnabled: Boolean = true,
+ val mncEnabled: Boolean = true,
+ val authTypeEnabled: Boolean = true,
+ val apnTypeEnabled: Boolean = true,
+ val apnProtocolEnabled: Boolean = true,
+ val apnRoamingEnabled: Boolean = true,
+ val apnEnableEnabled: Boolean = true,
+ val networkTypeEnabled: Boolean = true,
+ val mvnoTypeEnabled: Boolean = true,
+ val mvnoValueEnabled: Boolean = false,
+ val newApn: Boolean = false,
+)
+
+data class CustomizedConfig(
+ val newApn: Boolean = false,
+ val readOnlyApn: Boolean = false,
+ val isAddApnAllowed: Boolean = true,
+ val readOnlyApnTypes: List<String> = emptyList(),
+ val readOnlyApnFields: List<String> = emptyList(),
+ val defaultApnTypes: List<String> = emptyList(),
+ val defaultApnProtocol: String = "",
+ val defaultApnRoamingProtocol: String = "",
+)
+
+/**
+ * Initialize ApnData according to the arguments.
+ * @param arguments The data passed in when the user calls PageProvider.
+ * @param uriInit The decoded user incoming uri data in Page.
+ * @param subId The subId obtained in arguments.
+ *
+ * @return Initialized CustomizedConfig information.
+ */
+fun getApnDataInit(arguments: Bundle, context: Context, uriInit: Uri, subId: Int): ApnData {
+
+ val uriType = arguments.getString(URI_TYPE)!!
+ val mvnoType = arguments.getString(MVNO_TYPE)
+ val mvnoValue = arguments.getString(MVNO_MATCH_DATA)
+ val mvnoTypeOptions = context.resources.getStringArray(R.array.mvno_type_entries).toList()
+
+ val configManager =
+ context.getSystemService(Context.CARRIER_CONFIG_SERVICE) as CarrierConfigManager
+ getCarrierCustomizedConfig(configManager, subId)
+
+ if (!uriInit.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) {
+ Log.e(TAG, "Insert request not for carrier table. Uri: $uriInit")
+ return ApnData() //TODO: finish
+ }
+
+ var apnDataInit = when (uriType) {
+ EDIT_URL -> getApnDataFromUri(uriInit, context)
+ INSERT_URL -> ApnData(
+ mvnoType = mvnoTypeOptions.indexOf(mvnoType!!),
+ mvnoValue = mvnoValue!!
+ )
+
+ else -> ApnData() //TODO: finish
+ }
+
+ if (uriType == INSERT_URL) {
+ apnDataInit = apnDataInit.copy(newApn = true)
+ }
+
+ // TODO: mvnoDescription
+ apnDataInit = apnDataInit.copy(
+ apnEnableEnabled =
+ context.resources.getBoolean(R.bool.config_allow_edit_carrier_enabled)
+ )
+ // TODO: mIsCarrierIdApn & disableInit(apnDataInit)
+ return apnDataInit
}
+
+/**
+ * Initialize CustomizedConfig information through subId.
+ * @param subId subId information obtained from arguments.
+ *
+ * @return Initialized CustomizedConfig information.
+ */
+fun getCarrierCustomizedConfig(configManager: CarrierConfigManager, subId: Int): CustomizedConfig {
+ val b = configManager.getConfigForSubId(
+ subId,
+ CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY,
+ CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY,
+ CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY,
+ CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING,
+ CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING,
+ CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL
+ )
+ val customizedConfig = CustomizedConfig(
+ readOnlyApnTypes = b.getStringArray(
+ CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY
+ )?.toList() ?: emptyList(), readOnlyApnFields = b.getStringArray(
+ CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY
+ )?.toList() ?: emptyList(), defaultApnTypes = b.getStringArray(
+ CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY
+ )?.toList() ?: emptyList(), defaultApnProtocol = b.getString(
+ CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING
+ ) ?: "", defaultApnRoamingProtocol = b.getString(
+ CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING
+ ) ?: "", isAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL)
+ )
+ if (!ArrayUtils.isEmpty(customizedConfig.readOnlyApnTypes)) {
+ Log.d(
+ TAG,
+ "getCarrierCustomizedConfig: read only APN type: " + customizedConfig.readOnlyApnTypes.joinToString(
+ ", "
+ )
+ )
+ }
+ if (!ArrayUtils.isEmpty(customizedConfig.defaultApnTypes)) {
+ Log.d(
+ TAG,
+ "getCarrierCustomizedConfig: default apn types: " + customizedConfig.defaultApnTypes.joinToString(
+ ", "
+ )
+ )
+ }
+ if (!TextUtils.isEmpty(customizedConfig.defaultApnProtocol)) {
+ Log.d(
+ TAG,
+ "getCarrierCustomizedConfig: default apn protocol: ${customizedConfig.defaultApnProtocol}"
+ )
+ }
+ if (!TextUtils.isEmpty(customizedConfig.defaultApnRoamingProtocol)) {
+ Log.d(
+ TAG,
+ "getCarrierCustomizedConfig: default apn roaming protocol: ${customizedConfig.defaultApnRoamingProtocol}"
+ )
+ }
+ if (!customizedConfig.isAddApnAllowed) {
+ Log.d(TAG, "getCarrierCustomizedConfig: not allow to add new APN")
+ }
+ return customizedConfig
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
index 8902c73..8d1b223 100644
--- a/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnEditPageProviderTest.kt
@@ -256,9 +256,9 @@
.performScrollToNode(hasText(passwordTitle, true))
composeTestRule.onNodeWithText(passwordTitle, true).assertIsDisplayed()
}
-
+
private companion object {
const val NETWORK_TYPE_UNSPECIFIED = "Unspecified"
const val NETWORK_TYPE_LTE = "LTE"
}
-}
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
new file mode 100644
index 0000000..47f2a7f
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnRepositoryTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.apn
+
+import android.content.ContentResolver
+import android.content.Context
+import android.database.MatrixCursor
+import android.net.Uri
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class ApnRepositoryTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+ private val mContentResolver = mock<ContentResolver> {}
+ private val uri = mock<Uri> {}
+
+ @Test
+ fun getApnDataFromUri() {
+ // mock out resources and the feature provider
+ val cursor = MatrixCursor(sProjection)
+ cursor.addRow(
+ arrayOf<Any?>(
+ 0, "name", "apn", "proxy", "port",
+ "userName", "server", "passWord", "mmsc", "mcc", "mnc", "numeric",
+ "mmsProxy", "mmsPort", 0, "apnType", "apnProtocol", 0,
+ 0, "apnRoaming", "mvnoType", "mvnoValue", 0, 1, 0
+ )
+ )
+ val context = Mockito.spy(context)
+ whenever(context.contentResolver).thenReturn(mContentResolver)
+ whenever(mContentResolver.query(uri, sProjection, null, null, null)).thenReturn(cursor)
+ assert(getApnDataFromUri(uri, context).name == "name")
+ }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt
new file mode 100644
index 0000000..e590c28
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network.apn
+
+import android.os.PersistableBundle
+import android.telephony.CarrierConfigManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+class ApnStatusTest {
+ private val subId = 1
+ private val configManager = mock<CarrierConfigManager> {
+ val p = PersistableBundle()
+ p.putBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL, true)
+ on {
+ getConfigForSubId(
+ subId,
+ CarrierConfigManager.KEY_READ_ONLY_APN_TYPES_STRING_ARRAY,
+ CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY,
+ CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY,
+ CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING,
+ CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING,
+ CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL
+ )
+ } doReturn p
+ }
+
+ @Test
+ fun getCarrierCustomizedConfig_test() {
+ assert(getCarrierCustomizedConfig(configManager, subId).isAddApnAllowed)
+ }
+}
\ No newline at end of file