Refactor CellInfoUtil

Unify and simplify the logic, and add unit test.

Bug: 293845605
Test: manual - on Mobile Settings
Test: unit test
Change-Id: I5467b92baa8e47fbd400c7a21fd39fd5ec90ed3a
This commit is contained in:
Chaohui Wang
2024-01-03 12:42:30 +08:00
parent 1a564010bf
commit e3b527a2bf
5 changed files with 297 additions and 263 deletions

View File

@@ -1,219 +0,0 @@
/*
* 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;
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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
}
}

View File

@@ -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) {

View File

@@ -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();

View File

@@ -0,0 +1,173 @@
/*
* 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,
)
}
}