/* * 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 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 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 getRegisteredCellInfo(TelephonyManager defaultDataTelephonyManager) { List allList = defaultDataTelephonyManager.getAllCellInfo(); List cellInfoList = new ArrayList<>(); for (CellInfo ci : allList) { if (ci.isRegistered()) cellInfoList.add(ci); if (DBG) Log.v(TAG, ci.toString()); } return cellInfoList; } }