Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e9917e2f authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Fallback to NUMERIC when CARRIER_ID is unknown

For some subscription, the carrier id is unknown (-1), which cause the
inserted APN not be displayed.

Fallback to NUMERIC when CARRIER_ID is unknown to fix this issue.

Fix: 324394199
Test: manual with esim test profile - carrier id -1
Test: unit tests
Change-Id: Ic63a3e0c9ab37c8bdf86a2c7155b08778f05deff
parent faba808e
Loading
Loading
Loading
Loading
+32 −19
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.provider.Telephony
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Log
import com.android.settings.R
import com.android.settingslib.utils.ThreadUtils
@@ -43,7 +45,6 @@ const val NETWORK_TYPE_INDEX = 15
const val ROAMING_PROTOCOL_INDEX = 16
const val EDITED_INDEX = 17
const val USER_EDITABLE_INDEX = 18
const val CARRIER_ID_INDEX = 19

val sProjection = arrayOf(
    Telephony.Carriers._ID,  // 0
@@ -65,7 +66,6 @@ val sProjection = arrayOf(
    Telephony.Carriers.ROAMING_PROTOCOL,  // 16
    Telephony.Carriers.EDITED_STATUS,  // 17
    Telephony.Carriers.USER_EDITABLE,  // 18
    Telephony.Carriers.CARRIER_ID // 19
)

const val TAG = "ApnRepository"
@@ -109,7 +109,6 @@ fun getApnDataFromUri(uri: Uri, context: Context): ApnData {

            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,
@@ -130,7 +129,6 @@ fun getApnDataFromUri(uri: Uri, context: Context): ApnData {
                networkType = networkType,
                edited = edited,
                userEditable = userEditable,
                carrierId = carrierId
            )
        }
    }
@@ -199,25 +197,29 @@ fun updateApnDataToDatabase(
    }
}

fun isItemExist(apnData: ApnData, context: Context): String? {
    var contentValueMap = apnData.getContentValueMap(context)
    val removedList = arrayListOf(
        Telephony.Carriers.NAME, Telephony.Carriers.USER,
        Telephony.Carriers.SERVER, Telephony.Carriers.PASSWORD, Telephony.Carriers.AUTH_TYPE,
        Telephony.Carriers.TYPE, Telephony.Carriers.NETWORK_TYPE_BITMASK,
        Telephony.Carriers.CARRIER_ENABLED
/** Not allowing add duplicated items, if the values of the following keys are all identical. */
private val NonDuplicatedKeys = setOf(
    Telephony.Carriers.APN,
    Telephony.Carriers.PROXY,
    Telephony.Carriers.PORT,
    Telephony.Carriers.MMSC,
    Telephony.Carriers.MMSPROXY,
    Telephony.Carriers.MMSPORT,
    Telephony.Carriers.PROTOCOL,
    Telephony.Carriers.ROAMING_PROTOCOL,
)
    contentValueMap =
        contentValueMap.filterNot { removedList.contains(it.key) } as MutableMap<String, Any>

fun isItemExist(apnData: ApnData, context: Context): String? {
    val contentValueMap = apnData.getContentValueMap(context).filterKeys { it in NonDuplicatedKeys }
    val list = contentValueMap.entries.toList()
    val selection = list.joinToString(" AND ") { "${it.key} = ?" }
    val selectionArgs: Array<String> = list.map { it.value.toString() }.toTypedArray()
    context.contentResolver.query(
        Telephony.Carriers.CONTENT_URI,
        sProjection,
        selection /* selection */,
        selectionArgs /* selectionArgs */,
        null /* sortOrder */
        Uri.withAppendedPath(Telephony.Carriers.SIM_APN_URI, apnData.subId.toString()),
        /* projection = */ emptyArray(),
        selection,
        selectionArgs,
        /* sortOrder = */ null,
    )?.use { cursor ->
        if (cursor.count > 0) {
            return context.resources.getString(R.string.error_duplicate_apn_entry)
@@ -225,3 +227,14 @@ fun isItemExist(apnData: ApnData, context: Context): String? {
    }
    return null
}

fun Context.getApnIdMap(subId: Int): Map<String, Any> {
    val subInfo = getSystemService(SubscriptionManager::class.java)!!
        .getActiveSubscriptionInfo(subId)
    val carrierId = subInfo.carrierId
    return if (carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) {
        mapOf(Telephony.Carriers.CARRIER_ID to carrierId)
    } else {
        mapOf(Telephony.Carriers.NUMERIC to subInfo.mccString + subInfo.mncString)
    }.also { Log.d(TAG, "[$subId] New APN item with id: $it") }
}
+22 −31
Original line number Diff line number Diff line
@@ -22,13 +22,10 @@ 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 androidx.compose.runtime.snapshots.SnapshotStateList
import com.android.internal.util.ArrayUtils
import com.android.settings.R
import com.android.settings.network.apn.ApnNetworkTypes.getNetworkType
import com.android.settings.network.apn.ApnTypes.APN_TYPES
import com.android.settings.network.apn.ApnTypes.APN_TYPE_ALL
import com.android.settings.network.apn.ApnTypes.APN_TYPE_EMERGENCY
@@ -56,7 +53,6 @@ data class ApnData(
    val networkType: Long = 0,
    val edited: Int = Telephony.Carriers.USER_EDITED,
    val userEditable: Int = 1,
    val carrierId: Int = TelephonyManager.UNKNOWN_CARRIER_ID,
    val nameEnabled: Boolean = true,
    val apnEnabled: Boolean = true,
    val proxyEnabled: Boolean = true,
@@ -78,17 +74,17 @@ data class ApnData(
    val validEnabled: Boolean = false,
    val customizedConfig: CustomizedConfig = CustomizedConfig()
) {
    fun getContentValueMap(context: Context): MutableMap<String, Any> {
        val simCarrierId =
            context.getSystemService(TelephonyManager::class.java)!!
                .createForSubscriptionId(subId)
                .getSimCarrierId()
        return mutableMapOf(
            Telephony.Carriers.NAME to name, Telephony.Carriers.APN to apn,
            Telephony.Carriers.PROXY to proxy, Telephony.Carriers.PORT to port,
            Telephony.Carriers.MMSPROXY to mmsProxy, Telephony.Carriers.MMSPORT to mmsPort,
            Telephony.Carriers.USER to userName, Telephony.Carriers.SERVER to server,
            Telephony.Carriers.PASSWORD to passWord, Telephony.Carriers.MMSC to mmsc,
    fun getContentValueMap(context: Context): Map<String, Any> = mapOf(
        Telephony.Carriers.NAME to name,
        Telephony.Carriers.APN to apn,
        Telephony.Carriers.PROXY to proxy,
        Telephony.Carriers.PORT to port,
        Telephony.Carriers.USER to userName,
        Telephony.Carriers.SERVER to server,
        Telephony.Carriers.PASSWORD to passWord,
        Telephony.Carriers.MMSC to mmsc,
        Telephony.Carriers.MMSPROXY to mmsProxy,
        Telephony.Carriers.MMSPORT to mmsPort,
        Telephony.Carriers.AUTH_TYPE to authType,
        Telephony.Carriers.PROTOCOL to convertOptions2Protocol(apnProtocol, context),
        Telephony.Carriers.ROAMING_PROTOCOL to convertOptions2Protocol(apnRoaming, context),
@@ -96,16 +92,11 @@ data class ApnData(
        Telephony.Carriers.NETWORK_TYPE_BITMASK to networkType,
        Telephony.Carriers.CARRIER_ENABLED to apnEnable,
        Telephony.Carriers.EDITED_STATUS to Telephony.Carriers.USER_EDITED,
            Telephony.Carriers.CARRIER_ID to simCarrierId
    )
    }

    fun getContentValues(context: Context): ContentValues {
        val values = ContentValues()
        val contentValueMap = getContentValueMap(context)
        if (!newApn) contentValueMap.remove(Telephony.Carriers.CARRIER_ID)
        contentValueMap.forEach { (key, value) -> values.putObject(key, value) }
        return values
    fun getContentValues(context: Context) = ContentValues().apply {
        if (newApn) context.getApnIdMap(subId).forEach(::putObject)
        getContentValueMap(context).forEach(::putObject)
    }
}

+59 −10
Original line number Diff line number Diff line
@@ -20,19 +20,39 @@ import android.content.ContentResolver
import android.content.Context
import android.database.MatrixCursor
import android.net.Uri
import android.provider.Telephony
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
import org.mockito.kotlin.whenever

@RunWith(AndroidJUnit4::class)
class ApnRepositoryTest {

    private val context: Context = ApplicationProvider.getApplicationContext()
    private val mContentResolver = mock<ContentResolver> {}
    private val contentResolver = mock<ContentResolver>()

    private val mockSubscriptionInfo = mock<SubscriptionInfo> {
        on { mccString } doReturn MCC
        on { mncString } doReturn MNC
    }

    private val mockSubscriptionManager = mock<SubscriptionManager> {
        on { getActiveSubscriptionInfo(SUB_ID) } doReturn mockSubscriptionInfo
    }

    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
        on { contentResolver } doReturn contentResolver
        on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
    }
    private val uri = mock<Uri> {}

    @Test
@@ -40,7 +60,7 @@ class ApnRepositoryTest {
        // mock out resources and the feature provider
        val cursor = MatrixCursor(sProjection)
        cursor.addRow(
            arrayOf<Any?>(
            arrayOf<Any>(
                0,
                "name",
                "apn",
@@ -60,12 +80,41 @@ class ApnRepositoryTest {
                "apnRoaming",
                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")
        whenever(contentResolver.query(uri, sProjection, null, null, null)).thenReturn(cursor)

        val apnData = getApnDataFromUri(uri, context)

        assertThat(apnData.name).isEqualTo("name")
    }

    @Test
    fun getApnIdMap_knownCarrierId() {
        mockSubscriptionInfo.stub {
            on { carrierId } doReturn CARRIER_ID
        }

        val idMap = context.getApnIdMap(SUB_ID)

        assertThat(idMap).containsExactly(Telephony.Carriers.CARRIER_ID, CARRIER_ID)
    }

    @Test
    fun getApnIdMap_unknownCarrierId() {
        mockSubscriptionInfo.stub {
            on { carrierId } doReturn TelephonyManager.UNKNOWN_CARRIER_ID
        }

        val idMap = context.getApnIdMap(SUB_ID)

        assertThat(idMap).containsExactly(Telephony.Carriers.NUMERIC, MCC + MNC)
    }

    private companion object {
        const val SUB_ID = 2
        const val CARRIER_ID = 10
        const val MCC = "310"
        const val MNC = "101"
    }
}