| /* |
| * 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; |
| } |
| } |