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

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

Refactor CellInfoUtil

Unify and simplify the logic, and add unit test.

Bug: 293845605
Test: manual - on Mobile Settings
Test: unit test
Change-Id: I5467b92baa8e47fbd400c7a21fd39fd5ec90ed3a
parent 1a564010
Loading
Loading
Loading
Loading
+0 −219
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.telephony;

import android.telephony.CellIdentity;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
import android.telephony.CellIdentityNr;
import android.telephony.CellIdentityTdscdma;
import android.telephony.CellIdentityWcdma;
import android.telephony.CellInfo;
import android.telephony.CellInfoCdma;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
import android.telephony.CellInfoNr;
import android.telephony.CellInfoTdscdma;
import android.telephony.CellInfoWcdma;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
import android.text.TextUtils;

import com.android.internal.telephony.OperatorInfo;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Add static Utility functions to get information from the CellInfo object.
 * TODO: Modify {@link CellInfo} for simplify those functions
 */
public final class CellInfoUtil {
    private static final String TAG = "NetworkSelectSetting";

    private CellInfoUtil() {
    }

    /**
     * Returns the title of the network obtained in the manual search.
     *
     * @param cellId contains the identity of the network.
     * @param networkMccMnc contains the MCCMNC string of the network
     * @return Long Name if not null/empty, otherwise Short Name if not null/empty,
     * else MCCMNC string.
     */
    public static String getNetworkTitle(CellIdentity cellId, String networkMccMnc) {
        if (cellId != null) {
            String title = Objects.toString(cellId.getOperatorAlphaLong(), "");
            if (TextUtils.isEmpty(title)) {
                title = Objects.toString(cellId.getOperatorAlphaShort(), "");
            }
            if (!TextUtils.isEmpty(title)) {
                return title;
            }
        }
        if (TextUtils.isEmpty(networkMccMnc)) {
            return "";
        }
        final BidiFormatter bidiFormatter = BidiFormatter.getInstance();
        return bidiFormatter.unicodeWrap(networkMccMnc, TextDirectionHeuristics.LTR);
    }

    /**
     * Returns the CellIdentity from CellInfo
     *
     * @param cellInfo contains the information of the network.
     * @return CellIdentity within CellInfo
     */
    public static CellIdentity getCellIdentity(CellInfo cellInfo) {
        if (cellInfo == null) {
            return null;
        }
        CellIdentity cellId = null;
        if (cellInfo instanceof CellInfoGsm) {
            cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
        } else if (cellInfo instanceof CellInfoCdma) {
            cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
        } else if (cellInfo instanceof CellInfoWcdma) {
            cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
        } else if (cellInfo instanceof CellInfoTdscdma) {
            cellId = ((CellInfoTdscdma) cellInfo).getCellIdentity();
        } else if (cellInfo instanceof CellInfoLte) {
            cellId = ((CellInfoLte) cellInfo).getCellIdentity();
        } else if (cellInfo instanceof CellInfoNr) {
            cellId = ((CellInfoNr) cellInfo).getCellIdentity();
        }
        return cellId;
    }

    /**
     * Creates a CellInfo object from OperatorInfo. GsmCellInfo is used here only because
     * operatorInfo does not contain technology type while CellInfo is an abstract object that
     * requires to specify technology type. It doesn't matter which CellInfo type to use here, since
     * we only want to wrap the operator info and PLMN to a CellInfo object.
     */
    public static CellInfo convertOperatorInfoToCellInfo(OperatorInfo operatorInfo) {
        final String operatorNumeric = operatorInfo.getOperatorNumeric();
        String mcc = null;
        String mnc = null;
        if (operatorNumeric != null && operatorNumeric.matches("^[0-9]{5,6}$")) {
            mcc = operatorNumeric.substring(0, 3);
            mnc = operatorNumeric.substring(3);
        }
        final CellIdentityGsm cig = new CellIdentityGsm(
                Integer.MAX_VALUE /* lac */,
                Integer.MAX_VALUE /* cid */,
                Integer.MAX_VALUE /* arfcn */,
                Integer.MAX_VALUE /* bsic */,
                mcc,
                mnc,
                operatorInfo.getOperatorAlphaLong(),
                operatorInfo.getOperatorAlphaShort(),
                Collections.emptyList());

        final CellInfoGsm ci = new CellInfoGsm();
        ci.setCellIdentity(cig);
        return ci;
    }

    /** Convert a list of cellInfos to readable string without sensitive info. */
    public static String cellInfoListToString(List<CellInfo> cellInfos) {
        return cellInfos.stream()
                .map(cellInfo -> cellInfoToString(cellInfo))
                .collect(Collectors.joining(", "));
    }

    /** Convert {@code cellInfo} to a readable string without sensitive info. */
    public static String cellInfoToString(CellInfo cellInfo) {
        final String cellType = cellInfo.getClass().getSimpleName();
        final CellIdentity cid = getCellIdentity(cellInfo);
        String mcc = getCellIdentityMcc(cid);
        String mnc = getCellIdentityMnc(cid);
        CharSequence alphaLong = null;
        CharSequence alphaShort = null;
        if (cid != null) {
            alphaLong = cid.getOperatorAlphaLong();
            alphaShort = cid.getOperatorAlphaShort();
        }
        return String.format(
                "{CellType = %s, isRegistered = %b, mcc = %s, mnc = %s, alphaL = %s, alphaS = %s}",
                cellType, cellInfo.isRegistered(), mcc, mnc,
                alphaLong, alphaShort);
    }

    /**
     * Returns the MccMnc.
     *
     * @param cid contains the identity of the network.
     * @return MccMnc string.
     */
    public static String getCellIdentityMccMnc(CellIdentity cid) {
        String mcc = getCellIdentityMcc(cid);
        String mnc = getCellIdentityMnc(cid);
        return (mcc == null || mnc == null) ? null : mcc + mnc;
    }

    /**
     * Returns the Mcc.
     *
     * @param cid contains the identity of the network.
     * @return Mcc string.
     */
    public static String getCellIdentityMcc(CellIdentity cid) {
        String mcc = null;
        if (cid != null) {
            if (cid instanceof CellIdentityGsm) {
                mcc = ((CellIdentityGsm) cid).getMccString();
            } else if (cid instanceof CellIdentityWcdma) {
                mcc = ((CellIdentityWcdma) cid).getMccString();
            } else if (cid instanceof CellIdentityTdscdma) {
                mcc = ((CellIdentityTdscdma) cid).getMccString();
            } else if (cid instanceof CellIdentityLte) {
                mcc = ((CellIdentityLte) cid).getMccString();
            } else if (cid instanceof CellIdentityNr) {
                mcc = ((CellIdentityNr) cid).getMccString();
            }
        }
        return (mcc == null) ? null : mcc;
    }

    /**
     * Returns the Mnc.
     *
     * @param cid contains the identity of the network.
     * @return Mcc string.
     */
    public static String getCellIdentityMnc(CellIdentity cid) {
        String mnc = null;
        if (cid != null) {
            if (cid instanceof CellIdentityGsm) {
                mnc = ((CellIdentityGsm) cid).getMncString();
            } else if (cid instanceof CellIdentityWcdma) {
                mnc = ((CellIdentityWcdma) cid).getMncString();
            } else if (cid instanceof CellIdentityTdscdma) {
                mnc = ((CellIdentityTdscdma) cid).getMncString();
            } else if (cid instanceof CellIdentityLte) {
                mnc = ((CellIdentityLte) cid).getMncString();
            } else if (cid instanceof CellIdentityNr) {
                mnc = ((CellIdentityNr) cid).getMncString();
            }
        }
        return (mnc == null) ? null : mnc;
    }
}
+113 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.telephony

import android.telephony.CellIdentity
import android.telephony.CellIdentityGsm
import android.telephony.CellInfo
import android.telephony.CellInfoGsm
import android.text.BidiFormatter
import android.text.TextDirectionHeuristics
import com.android.internal.telephony.OperatorInfo

/**
 * Add static Utility functions to get information from the CellInfo object.
 * TODO: Modify [CellInfo] for simplify those functions
 */
object CellInfoUtil {

    /**
     * Returns the title of the network obtained in the manual search.
     *
     * By the following order,
     * 1. Long Name if not null/empty
     * 2. Short Name if not null/empty
     * 3. OperatorNumeric (MCCMNC) string
     */
    @JvmStatic
    fun CellIdentity.getNetworkTitle(): String? {
        operatorAlphaLong?.takeIf { it.isNotBlank() }?.let { return it.toString() }
        operatorAlphaShort?.takeIf { it.isNotBlank() }?.let { return it.toString() }
        val operatorNumeric = getOperatorNumeric() ?: return null
        val bidiFormatter = BidiFormatter.getInstance()
        return bidiFormatter.unicodeWrap(operatorNumeric, TextDirectionHeuristics.LTR)
    }

    /**
     * Creates a CellInfo object from OperatorInfo. GsmCellInfo is used here only because
     * operatorInfo does not contain technology type while CellInfo is an abstract object that
     * requires to specify technology type. It doesn't matter which CellInfo type to use here, since
     * we only want to wrap the operator info and PLMN to a CellInfo object.
     */
    @JvmStatic
    fun convertOperatorInfoToCellInfo(operatorInfo: OperatorInfo): CellInfo {
        val operatorNumeric = operatorInfo.operatorNumeric
        var mcc: String? = null
        var mnc: String? = null
        if (operatorNumeric?.matches("^[0-9]{5,6}$".toRegex()) == true) {
            mcc = operatorNumeric.substring(0, 3)
            mnc = operatorNumeric.substring(3)
        }
        return CellInfoGsm().apply {
            cellIdentity = CellIdentityGsm(
                /* lac = */ Int.MAX_VALUE,
                /* cid = */ Int.MAX_VALUE,
                /* arfcn = */ Int.MAX_VALUE,
                /* bsic = */ Int.MAX_VALUE,
                /* mccStr = */ mcc,
                /* mncStr = */ mnc,
                /* alphal = */ operatorInfo.operatorAlphaLong,
                /* alphas = */ operatorInfo.operatorAlphaShort,
                /* additionalPlmns = */ emptyList(),
            )
        }
    }

    /**
     * Convert a list of cellInfos to readable string without sensitive info.
     */
    @JvmStatic
    fun cellInfoListToString(cellInfos: List<CellInfo>): String =
        cellInfos.joinToString { cellInfo -> cellInfo.readableString() }

    /**
     * Convert [CellInfo] to a readable string without sensitive info.
     */
    private fun CellInfo.readableString(): String = buildString {
        append("{CellType = ${this@readableString::class.simpleName}, ")
        append("isRegistered = $isRegistered, ")
        append(cellIdentity.readableString())
        append("}")
    }

    private fun CellIdentity.readableString(): String = buildString {
        append("mcc = $mccString, ")
        append("mnc = $mncString, ")
        append("alphaL = $operatorAlphaLong, ")
        append("alphaS = $operatorAlphaShort")
    }

    /**
     * Returns the MccMnc.
     */
    @JvmStatic
    fun CellIdentity.getOperatorNumeric(): String? {
        val mcc = mccString
        val mnc = mncString
        return if (mcc == null || mnc == null) null else mcc + mnc
    }
}
+9 −40
Original line number Diff line number Diff line
@@ -18,14 +18,11 @@ package com.android.settings.network.telephony;

import static android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS;

import static com.android.settings.network.telephony.CellInfoUtil.getOperatorNumeric;

import android.content.Context;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.CellIdentity;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
import android.telephony.CellIdentityNr;
import android.telephony.CellIdentityTdscdma;
import android.telephony.CellIdentityWcdma;
import android.telephony.CellInfo;
import android.telephony.CellInfoCdma;
import android.telephony.CellInfoGsm;
@@ -36,6 +33,7 @@ import android.telephony.CellInfoWcdma;
import android.telephony.CellSignalStrength;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;

@@ -87,7 +85,7 @@ public class NetworkOperatorPreference extends Preference {
     * Change cell information
     */
    public void updateCell(CellInfo cellinfo) {
        updateCell(cellinfo, CellInfoUtil.getCellIdentity(cellinfo));
        updateCell(cellinfo, cellinfo.getCellIdentity());
    }

    @VisibleForTesting
@@ -104,14 +102,14 @@ public class NetworkOperatorPreference extends Preference {
        if (cellinfo == null) {
            return false;
        }
        return mCellId.equals(CellInfoUtil.getCellIdentity(cellinfo));
        return mCellId.equals(cellinfo.getCellIdentity());
    }

    /**
     * Return true when this preference is for forbidden network
     */
    public boolean isForbiddenNetwork() {
        return ((mForbiddenPlmns != null) && mForbiddenPlmns.contains(getOperatorNumeric()));
        return ((mForbiddenPlmns != null) && mForbiddenPlmns.contains(getOperatorNumeric(mCellId)));
    }

    /**
@@ -147,41 +145,12 @@ public class NetworkOperatorPreference extends Preference {
        updateIcon(level);
    }

    /**
     * Operator numeric of this cell
     */
    public String getOperatorNumeric() {
        final CellIdentity cellId = mCellId;
        if (cellId == null) {
            return null;
        }
        if (cellId instanceof CellIdentityGsm) {
            return ((CellIdentityGsm) cellId).getMobileNetworkOperator();
        }
        if (cellId instanceof CellIdentityWcdma) {
            return ((CellIdentityWcdma) cellId).getMobileNetworkOperator();
        }
        if (cellId instanceof CellIdentityTdscdma) {
            return ((CellIdentityTdscdma) cellId).getMobileNetworkOperator();
        }
        if (cellId instanceof CellIdentityLte) {
            return ((CellIdentityLte) cellId).getMobileNetworkOperator();
        }
        if (cellId instanceof CellIdentityNr) {
            final String mcc = ((CellIdentityNr) cellId).getMccString();
            if (mcc == null) {
                return null;
            }
            return mcc.concat(((CellIdentityNr) cellId).getMncString());
        }
        return null;
    }

    /**
     * Operator name of this cell
     */
    @Nullable
    public String getOperatorName() {
        return CellInfoUtil.getNetworkTitle(mCellId, getOperatorNumeric());
        return CellInfoUtil.getNetworkTitle(mCellId);
    }

    /**
@@ -190,7 +159,7 @@ public class NetworkOperatorPreference extends Preference {
    public OperatorInfo getOperatorInfo() {
        return new OperatorInfo(Objects.toString(mCellId.getOperatorAlphaLong(), ""),
                Objects.toString(mCellId.getOperatorAlphaShort(), ""),
                getOperatorNumeric(), getAccessNetworkTypeFromCellInfo(mCellInfo));
                getOperatorNumeric(mCellId), getAccessNetworkTypeFromCellInfo(mCellInfo));
    }

    private int getIconIdForCell(CellInfo ci) {
+2 −4
Original line number Diff line number Diff line
@@ -365,14 +365,12 @@ public class NetworkSelectSettings extends DashboardFragment {
        }
        ArrayList<CellInfo> aggregatedList = new ArrayList<>();
        for (CellInfo cellInfo : cellInfoListInput) {
            String plmn = CellInfoUtil.getNetworkTitle(cellInfo.getCellIdentity(),
                    CellInfoUtil.getCellIdentityMccMnc(cellInfo.getCellIdentity()));
            String plmn = CellInfoUtil.getNetworkTitle(cellInfo.getCellIdentity());
            Class className = cellInfo.getClass();

            Optional<CellInfo> itemInTheList = aggregatedList.stream().filter(
                    item -> {
                        String itemPlmn = CellInfoUtil.getNetworkTitle(item.getCellIdentity(),
                                CellInfoUtil.getCellIdentityMccMnc(item.getCellIdentity()));
                        String itemPlmn = CellInfoUtil.getNetworkTitle(item.getCellIdentity());
                        return itemPlmn.equals(plmn) && item.getClass().equals(className);
                    })
                    .findFirst();
+173 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.telephony

import android.telephony.CellIdentityCdma
import android.telephony.CellIdentityGsm
import android.telephony.CellInfoCdma
import android.telephony.CellInfoGsm
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.internal.telephony.OperatorInfo
import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle
import com.android.settings.network.telephony.CellInfoUtil.getOperatorNumeric
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class CellInfoUtilTest {

    @Test
    fun getNetworkTitle_alphaLong() {
        val networkTitle = CELL_IDENTITY_GSM.getNetworkTitle()

        assertThat(networkTitle).isEqualTo(LONG)
    }

    @Test
    fun getNetworkTitle_alphaShort() {
        val cellIdentity = CellIdentityGsm(
            /* lac = */ 1,
            /* cid = */ 2,
            /* arfcn = */ 3,
            /* bsic = */ 4,
            /* mccStr = */ "123",
            /* mncStr = */ "01",
            /* alphal = */ "",
            /* alphas = */ SHORT,
            /* additionalPlmns = */ emptyList(),
        )

        val networkTitle = cellIdentity.getNetworkTitle()

        assertThat(networkTitle).isEqualTo(SHORT)
    }

    @Test
    fun getNetworkTitle_operatorNumeric() {
        val cellIdentity = CellIdentityGsm(
            /* lac = */ 1,
            /* cid = */ 2,
            /* arfcn = */ 3,
            /* bsic = */ 4,
            /* mccStr = */ "123",
            /* mncStr = */ "01",
            /* alphal = */ "",
            /* alphas = */ "",
            /* additionalPlmns = */ emptyList(),
        )

        val networkTitle = cellIdentity.getNetworkTitle()

        assertThat(networkTitle).isEqualTo("12301")
    }

    @Test
    fun getNetworkTitle_null() {
        val cellIdentity = CellIdentityGsm(
            /* lac = */ 1,
            /* cid = */ 2,
            /* arfcn = */ 3,
            /* bsic = */ 4,
            /* mccStr = */ null,
            /* mncStr = */ null,
            /* alphal = */ null,
            /* alphas = */ null,
            /* additionalPlmns = */ emptyList(),
        )

        val networkTitle = cellIdentity.getNetworkTitle()

        assertThat(networkTitle).isNull()
    }

    @Test
    fun convertOperatorInfoToCellInfo() {
        val operatorInfo = OperatorInfo(LONG, SHORT, "12301")

        val cellInfo = CellInfoUtil.convertOperatorInfoToCellInfo(operatorInfo)

        assertThat(cellInfo.cellIdentity.mccString).isEqualTo("123")
        assertThat(cellInfo.cellIdentity.mncString).isEqualTo("01")
        assertThat(cellInfo.cellIdentity.operatorAlphaLong).isEqualTo(LONG)
        assertThat(cellInfo.cellIdentity.operatorAlphaShort).isEqualTo(SHORT)
    }

    @Test
    fun cellInfoListToString() {
        val cellInfoList =
            listOf(
                CellInfoCdma().apply {
                    cellIdentity = CELL_IDENTITY_CDMA
                },
                CellInfoGsm().apply {
                    isRegistered = true
                    cellIdentity = CELL_IDENTITY_GSM
                },
            )

        val string = CellInfoUtil.cellInfoListToString(cellInfoList)

        assertThat(string).isEqualTo(
            "{CellType = CellInfoCdma, isRegistered = false, " +
                "mcc = null, mnc = null, alphaL = Long, alphaS = Short}, " +
                "{CellType = CellInfoGsm, isRegistered = true, " +
                "mcc = 123, mnc = 01, alphaL = Long, alphaS = Short}"
        )
    }

    @Test
    fun getOperatorNumeric_cdma() {
        val operatorNumeric = CELL_IDENTITY_CDMA.getOperatorNumeric()

        assertThat(operatorNumeric).isNull()
    }

    @Test
    fun getOperatorNumeric_gsm() {
        val operatorNumeric = CELL_IDENTITY_GSM.getOperatorNumeric()

        assertThat(operatorNumeric).isEqualTo("12301")
    }

    private companion object {
        const val LONG = "Long"
        const val SHORT = "Short"

        val CELL_IDENTITY_GSM = CellIdentityGsm(
            /* lac = */ 1,
            /* cid = */ 2,
            /* arfcn = */ 3,
            /* bsic = */ 4,
            /* mccStr = */ "123",
            /* mncStr = */ "01",
            /* alphal = */ LONG,
            /* alphas = */ SHORT,
            /* additionalPlmns = */ emptyList(),
        )

        val CELL_IDENTITY_CDMA = CellIdentityCdma(
            /* nid = */ 1,
            /* sid = */ 2,
            /* bid = */ 3,
            /* lon = */ 4,
            /* lat = */ 5,
            /* alphal = */ LONG,
            /* alphas = */ SHORT,
        )
    }
}