blob: 81d219ccecd7fc3b22162fccafc4f6469898b082 [file] [log] [blame]
/*
* Copyright 2019 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.server.wifi;
import static android.telephony.TelephonyManager.NETWORK_TYPE_CDMA;
import static android.telephony.TelephonyManager.NETWORK_TYPE_EVDO_0;
import static android.telephony.TelephonyManager.NETWORK_TYPE_GSM;
import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
import static android.telephony.TelephonyManager.NETWORK_TYPE_NR;
import static android.telephony.TelephonyManager.NETWORK_TYPE_TD_SCDMA;
import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS;
import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
import android.content.Context;
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.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
import android.telephony.CellSignalStrengthGsm;
import android.telephony.CellSignalStrengthLte;
import android.telephony.CellSignalStrengthNr;
import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* A class collecting the latest cellular link layer stats
*/
public class CellularLinkLayerStatsCollector {
private static final String TAG = "CellStatsCollector";
private static final boolean DBG = false;
private Context mContext;
private SubscriptionManager mSubManager = null;
private TelephonyManager mCachedDefaultDataTelephonyManager = null;
private int mCachedDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private CellInfo mLastPrimaryCellInfo = null;
private int mLastDataNetworkType = NETWORK_TYPE_UNKNOWN;
public CellularLinkLayerStatsCollector(Context context) {
mContext = context;
}
/**
* Get the latest DataNetworkType, SignalStrength, CellInfo and other information from
* default data sim's TelephonyManager, parse the values of primary registered cell and return
* them as an instance of CellularLinkLayerStats
*/
public CellularLinkLayerStats update() {
CellularLinkLayerStats cellStats = new CellularLinkLayerStats();
retrieveDefaultDataTelephonyManager();
if (mCachedDefaultDataTelephonyManager == null) {
if (DBG) Log.v(TAG, cellStats.toString());
return cellStats;
}
SignalStrength signalStrength = mCachedDefaultDataTelephonyManager.getSignalStrength();
List<CellSignalStrength> cssList = null;
if (signalStrength != null) cssList = signalStrength.getCellSignalStrengths();
if (mCachedDefaultDataTelephonyManager.getDataNetworkType() == NETWORK_TYPE_UNKNOWN
|| cssList == null || cssList.size() == 0) {
mLastPrimaryCellInfo = null;
mLastDataNetworkType = NETWORK_TYPE_UNKNOWN;
if (DBG) Log.v(TAG, cellStats.toString());
return cellStats;
}
if (DBG) Log.v(TAG, "Cell Signal Strength List size = " + cssList.size());
CellSignalStrength primaryCss = cssList.get(0);
cellStats.setSignalStrengthDbm(primaryCss.getDbm());
updateSignalStrengthDbAndNetworkTypeOfCellStats(primaryCss, cellStats);
int networkType = cellStats.getDataNetworkType();
CellInfo primaryCellInfo = getPrimaryCellInfo(mCachedDefaultDataTelephonyManager,
networkType);
boolean isSameRegisteredCell = getIsSameRegisteredCell(primaryCellInfo, networkType);
cellStats.setIsSameRegisteredCell(isSameRegisteredCell);
// Update for the next call
mLastPrimaryCellInfo = primaryCellInfo;
mLastDataNetworkType = networkType;
if (DBG) Log.v(TAG, cellStats.toString());
return cellStats;
}
private void retrieveDefaultDataTelephonyManager() {
if (!initSubManager()) return;
int defaultDataSubId = mSubManager.getDefaultDataSubscriptionId();
if (DBG) Log.v(TAG, "default Data Sub ID = " + defaultDataSubId);
if (defaultDataSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
mCachedDefaultDataTelephonyManager = null;
return;
}
if (defaultDataSubId != mCachedDefaultDataSubId
|| mCachedDefaultDataTelephonyManager == null) {
mCachedDefaultDataSubId = defaultDataSubId;
// TODO(b/132188983): Inject this using WifiInjector
TelephonyManager defaultSubTelephonyManager = (TelephonyManager) mContext
.getSystemService(Context.TELEPHONY_SERVICE);
if (defaultDataSubId == mSubManager.getDefaultSubscriptionId()) {
mCachedDefaultDataTelephonyManager = defaultSubTelephonyManager;
} else {
mCachedDefaultDataTelephonyManager = defaultSubTelephonyManager
.createForSubscriptionId(defaultDataSubId);
}
}
}
private boolean initSubManager() {
if (mSubManager == null) {
mSubManager = (SubscriptionManager) mContext.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
}
return (mSubManager != null);
}
/**
* Update dB value and network type base on CellSignalStrength subclass type.
* It follows the same order as that in SignalStrength.getPrimary().
* TODO: NR may move up in the future
*/
private void updateSignalStrengthDbAndNetworkTypeOfCellStats(CellSignalStrength primaryCss,
CellularLinkLayerStats cellStats) {
if (primaryCss instanceof CellSignalStrengthLte) {
CellSignalStrengthLte cssLte = (CellSignalStrengthLte) primaryCss;
cellStats.setSignalStrengthDb(cssLte.getRsrq());
cellStats.setDataNetworkType(NETWORK_TYPE_LTE);
} else if (primaryCss instanceof CellSignalStrengthCdma) {
CellSignalStrengthCdma cssCdma = (CellSignalStrengthCdma) primaryCss;
int evdoSnr = cssCdma.getEvdoSnr();
int cdmaEcio = cssCdma.getCdmaEcio();
if (evdoSnr != SignalStrength.INVALID) {
cellStats.setSignalStrengthDb(evdoSnr);
cellStats.setDataNetworkType(NETWORK_TYPE_EVDO_0);
} else {
cellStats.setSignalStrengthDb(cdmaEcio);
cellStats.setDataNetworkType(NETWORK_TYPE_CDMA);
}
} else if (primaryCss instanceof CellSignalStrengthTdscdma) {
cellStats.setDataNetworkType(NETWORK_TYPE_TD_SCDMA);
} else if (primaryCss instanceof CellSignalStrengthWcdma) {
CellSignalStrengthWcdma cssWcdma = (CellSignalStrengthWcdma) primaryCss;
cellStats.setSignalStrengthDb(cssWcdma.getEcNo());
cellStats.setDataNetworkType(NETWORK_TYPE_UMTS);
} else if (primaryCss instanceof CellSignalStrengthGsm) {
cellStats.setDataNetworkType(NETWORK_TYPE_GSM);
} else if (primaryCss instanceof CellSignalStrengthNr) {
CellSignalStrengthNr cssNr = (CellSignalStrengthNr) primaryCss;
cellStats.setSignalStrengthDb(cssNr.getCsiSinr());
cellStats.setDataNetworkType(NETWORK_TYPE_NR);
} else {
Log.e(TAG, "invalid CellSignalStrength");
}
}
private CellInfo getPrimaryCellInfo(TelephonyManager defaultDataTelephonyManager,
int networkType) {
List<CellInfo> cellInfoList = getRegisteredCellInfo(defaultDataTelephonyManager);
int cilSize = cellInfoList.size();
CellInfo primaryCellInfo = null;
// CellInfo.getCellConnectionStatus() should tell if it is primary serving cell.
// However, it currently always returns 0 (CONNECTION_NONE) for registered cells.
// Therefore, the workaround of deriving primary serving cell is
// to check if the registered cellInfo subclass type matches networkType
for (int i = 0; i < cilSize; ++i) {
CellInfo cellInfo = cellInfoList.get(i);
if ((cellInfo instanceof CellInfoTdscdma && networkType == NETWORK_TYPE_TD_SCDMA)
|| (cellInfo instanceof CellInfoCdma && (networkType == NETWORK_TYPE_CDMA
|| networkType == NETWORK_TYPE_EVDO_0))
|| (cellInfo instanceof CellInfoLte && networkType == NETWORK_TYPE_LTE)
|| (cellInfo instanceof CellInfoWcdma && networkType == NETWORK_TYPE_UMTS)
|| (cellInfo instanceof CellInfoGsm && networkType == NETWORK_TYPE_GSM)
|| (cellInfo instanceof CellInfoNr && networkType == NETWORK_TYPE_NR)) {
primaryCellInfo = cellInfo;
}
}
return primaryCellInfo;
}
private boolean getIsSameRegisteredCell(CellInfo primaryCellInfo, int networkType) {
boolean isSameRegisteredCell;
if (primaryCellInfo != null && mLastPrimaryCellInfo != null) {
isSameRegisteredCell = primaryCellInfo.getCellIdentity()
.equals(mLastPrimaryCellInfo.getCellIdentity());
} else if (primaryCellInfo == null && mLastPrimaryCellInfo == null) {
// This is a workaround when it can't find primaryCellInfo for two consecutive times.
isSameRegisteredCell = true;
} else {
// only one of them is null and it is a strong indication of primary cell change.
isSameRegisteredCell = false;
}
if (mLastDataNetworkType == NETWORK_TYPE_UNKNOWN || mLastDataNetworkType != networkType) {
isSameRegisteredCell = false;
}
return isSameRegisteredCell;
}
private List<CellInfo> getRegisteredCellInfo(TelephonyManager defaultDataTelephonyManager) {
List<CellInfo> allList = defaultDataTelephonyManager.getAllCellInfo();
List<CellInfo> cellInfoList = new ArrayList<>();
for (CellInfo ci : allList) {
if (ci.isRegistered()) cellInfoList.add(ci);
if (DBG) Log.v(TAG, ci.toString());
}
return cellInfoList;
}
}