1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony.metrics; 18 19 import android.annotation.Nullable; 20 import android.os.SystemClock; 21 import android.telephony.AccessNetworkConstants; 22 import android.telephony.AccessNetworkUtils; 23 import android.telephony.Annotation.NetworkType; 24 import android.telephony.NetworkRegistrationInfo; 25 import android.telephony.ServiceState; 26 import android.telephony.TelephonyManager; 27 28 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS; 29 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS; 30 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.internal.telephony.Phone; 34 import com.android.internal.telephony.PhoneFactory; 35 import com.android.internal.telephony.ServiceStateTracker; 36 import com.android.internal.telephony.imsphone.ImsPhone; 37 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch; 38 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState; 39 import com.android.telephony.Rlog; 40 41 import java.util.concurrent.atomic.AtomicReference; 42 43 /** Tracks service state duration and switch metrics for each phone. */ 44 public class ServiceStateStats { 45 private static final String TAG = ServiceStateStats.class.getSimpleName(); 46 47 private final AtomicReference<TimestampedServiceState> mLastState = 48 new AtomicReference<>(new TimestampedServiceState(null, 0L)); 49 private final Phone mPhone; 50 private final PersistAtomsStorage mStorage; 51 ServiceStateStats(Phone phone)52 public ServiceStateStats(Phone phone) { 53 mPhone = phone; 54 mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage(); 55 } 56 57 /** Finalizes the durations of the current service state segment. */ conclude()58 public void conclude() { 59 final long now = getTimeMillis(); 60 TimestampedServiceState lastState = 61 mLastState.getAndUpdate( 62 state -> new TimestampedServiceState(state.mServiceState, now)); 63 addServiceState(lastState, now); 64 } 65 66 /** Updates service state when IMS voice registration changes. */ onImsVoiceRegistrationChanged()67 public void onImsVoiceRegistrationChanged() { 68 final long now = getTimeMillis(); 69 TimestampedServiceState lastState = 70 mLastState.getAndUpdate( 71 state -> { 72 if (state.mServiceState == null) { 73 return new TimestampedServiceState(null, now); 74 } 75 CellularServiceState newServiceState = copyOf(state.mServiceState); 76 newServiceState.voiceRat = 77 getVoiceRat(mPhone, getServiceStateForPhone(mPhone)); 78 return new TimestampedServiceState(newServiceState, now); 79 }); 80 addServiceState(lastState, now); 81 } 82 83 /** Updates the current service state. */ onServiceStateChanged(ServiceState serviceState)84 public void onServiceStateChanged(ServiceState serviceState) { 85 final long now = getTimeMillis(); 86 if (isModemOff(serviceState)) { 87 // Finish the duration of last service state and mark modem off 88 addServiceState(mLastState.getAndSet(new TimestampedServiceState(null, now)), now); 89 } else { 90 CellularServiceState newState = new CellularServiceState(); 91 newState.voiceRat = getVoiceRat(mPhone, serviceState); 92 newState.dataRat = getRat(serviceState, NetworkRegistrationInfo.DOMAIN_PS); 93 newState.voiceRoamingType = serviceState.getVoiceRoamingType(); 94 newState.dataRoamingType = serviceState.getDataRoamingType(); 95 newState.isEndc = isEndc(serviceState); 96 newState.simSlotIndex = mPhone.getPhoneId(); 97 newState.isMultiSim = SimSlotState.isMultiSim(); 98 newState.carrierId = mPhone.getCarrierId(); 99 newState.isEmergencyOnly = isEmergencyOnly(serviceState); 100 101 TimestampedServiceState prevState = 102 mLastState.getAndSet(new TimestampedServiceState(newState, now)); 103 addServiceStateAndSwitch( 104 prevState, now, getDataServiceSwitch(prevState.mServiceState, newState)); 105 } 106 } 107 addServiceState(TimestampedServiceState prevState, long now)108 private void addServiceState(TimestampedServiceState prevState, long now) { 109 addServiceStateAndSwitch(prevState, now, null); 110 } 111 addServiceStateAndSwitch( TimestampedServiceState prevState, long now, @Nullable CellularDataServiceSwitch serviceSwitch)112 private void addServiceStateAndSwitch( 113 TimestampedServiceState prevState, 114 long now, 115 @Nullable CellularDataServiceSwitch serviceSwitch) { 116 if (prevState.mServiceState == null) { 117 // Skip duration when modem is off 118 return; 119 } 120 if (now >= prevState.mTimestamp) { 121 CellularServiceState state = copyOf(prevState.mServiceState); 122 state.totalTimeMillis = now - prevState.mTimestamp; 123 mStorage.addCellularServiceStateAndCellularDataServiceSwitch(state, serviceSwitch); 124 } else { 125 Rlog.e(TAG, "addServiceState: durationMillis<0"); 126 } 127 } 128 129 @Nullable getDataServiceSwitch( @ullable CellularServiceState prevState, CellularServiceState nextState)130 private CellularDataServiceSwitch getDataServiceSwitch( 131 @Nullable CellularServiceState prevState, CellularServiceState nextState) { 132 // Record switch only if multi-SIM state and carrier ID are the same and data RAT differs. 133 if (prevState != null 134 && prevState.isMultiSim == nextState.isMultiSim 135 && prevState.carrierId == nextState.carrierId 136 && prevState.dataRat != nextState.dataRat) { 137 CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch(); 138 serviceSwitch.ratFrom = prevState.dataRat; 139 serviceSwitch.ratTo = nextState.dataRat; 140 serviceSwitch.isMultiSim = nextState.isMultiSim; 141 serviceSwitch.simSlotIndex = nextState.simSlotIndex; 142 serviceSwitch.carrierId = nextState.carrierId; 143 serviceSwitch.switchCount = 1; 144 return serviceSwitch; 145 } else { 146 return null; 147 } 148 } 149 150 /** Returns the service state for the given phone, or {@code null} if it cannot be obtained. */ 151 @Nullable getServiceStateForPhone(Phone phone)152 private static ServiceState getServiceStateForPhone(Phone phone) { 153 ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker(); 154 return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null; 155 } 156 157 /** 158 * Returns the band used from the given phone, or {@code 0} if it is invalid or cannot be 159 * determined. 160 */ getBand(Phone phone)161 static int getBand(Phone phone) { 162 ServiceState serviceState = getServiceStateForPhone(phone); 163 return getBand(serviceState); 164 } 165 166 /** 167 * Returns the band used from the given service state, or {@code 0} if it is invalid or cannot 168 * be determined. 169 */ getBand(@ullable ServiceState serviceState)170 static int getBand(@Nullable ServiceState serviceState) { 171 if (serviceState == null) { 172 Rlog.w(TAG, "getBand: serviceState=null"); 173 return 0; // Band unknown 174 } 175 int chNumber = serviceState.getChannelNumber(); 176 int band; 177 @NetworkType int rat = getRat(serviceState, NetworkRegistrationInfo.DOMAIN_PS); 178 if (rat == TelephonyManager.NETWORK_TYPE_UNKNOWN) { 179 rat = serviceState.getVoiceNetworkType(); 180 } 181 switch (rat) { 182 case TelephonyManager.NETWORK_TYPE_GSM: 183 case TelephonyManager.NETWORK_TYPE_GPRS: 184 case TelephonyManager.NETWORK_TYPE_EDGE: 185 band = AccessNetworkUtils.getOperatingBandForArfcn(chNumber); 186 break; 187 case TelephonyManager.NETWORK_TYPE_UMTS: 188 case TelephonyManager.NETWORK_TYPE_HSDPA: 189 case TelephonyManager.NETWORK_TYPE_HSUPA: 190 case TelephonyManager.NETWORK_TYPE_HSPA: 191 case TelephonyManager.NETWORK_TYPE_HSPAP: 192 band = AccessNetworkUtils.getOperatingBandForUarfcn(chNumber); 193 break; 194 case TelephonyManager.NETWORK_TYPE_LTE: 195 case TelephonyManager.NETWORK_TYPE_LTE_CA: 196 band = AccessNetworkUtils.getOperatingBandForEarfcn(chNumber); 197 break; 198 case TelephonyManager.NETWORK_TYPE_NR: 199 band = AccessNetworkUtils.getOperatingBandForNrarfcn(chNumber); 200 break; 201 default: 202 Rlog.w(TAG, "getBand: unknown WWAN RAT " + rat); 203 band = 0; 204 break; 205 } 206 if (band == AccessNetworkUtils.INVALID_BAND) { 207 Rlog.w(TAG, "getBand: band invalid for rat=" + rat + " ch=" + chNumber); 208 return 0; 209 } else { 210 return band; 211 } 212 } 213 copyOf(CellularServiceState state)214 private static CellularServiceState copyOf(CellularServiceState state) { 215 // MessageNano does not support clone, have to copy manually 216 CellularServiceState copy = new CellularServiceState(); 217 copy.voiceRat = state.voiceRat; 218 copy.dataRat = state.dataRat; 219 copy.voiceRoamingType = state.voiceRoamingType; 220 copy.dataRoamingType = state.dataRoamingType; 221 copy.isEndc = state.isEndc; 222 copy.simSlotIndex = state.simSlotIndex; 223 copy.isMultiSim = state.isMultiSim; 224 copy.carrierId = state.carrierId; 225 copy.totalTimeMillis = state.totalTimeMillis; 226 copy.isEmergencyOnly = state.isEmergencyOnly; 227 return copy; 228 } 229 230 /** 231 * Returns {@code true} if modem radio is turned off (e.g. airplane mode). 232 * 233 * <p>Currently this is approximated by voice service state being {@code STATE_POWER_OFF}. 234 */ isModemOff(ServiceState state)235 private static boolean isModemOff(ServiceState state) { 236 // TODO(b/189335473): we should get this info from phone's radio power state, which is 237 // updated separately 238 return state.getVoiceRegState() == ServiceState.STATE_POWER_OFF; 239 } 240 241 /** 242 * Returns the current voice RAT from IMS registration if present, otherwise from the service 243 * state. 244 * 245 * <p>If the device is not in service, {@code TelephonyManager.NETWORK_TYPE_UNKNOWN} is returned 246 * despite that the device may have emergency service over a certain RAT. 247 */ getVoiceRat(Phone phone, @Nullable ServiceState state)248 static @NetworkType int getVoiceRat(Phone phone, @Nullable ServiceState state) { 249 return getVoiceRat(phone, state, VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN); 250 } 251 252 /** 253 * Returns the current voice RAT according to the bearer. 254 * 255 * <p>If the device is not in service, {@code TelephonyManager.NETWORK_TYPE_UNKNOWN} is returned 256 * despite that the device may have emergency service over a certain RAT. 257 */ 258 @VisibleForTesting public getVoiceRat(Phone phone, @Nullable ServiceState state, int bearer)259 static @NetworkType int getVoiceRat(Phone phone, @Nullable ServiceState state, int bearer) { 260 if (state == null) { 261 return TelephonyManager.NETWORK_TYPE_UNKNOWN; 262 } 263 ImsPhone imsPhone = (ImsPhone) phone.getImsPhone(); 264 if (bearer != VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS && imsPhone != null) { 265 @NetworkType int imsVoiceRat = imsPhone.getImsStats().getImsVoiceRadioTech(); 266 if (imsVoiceRat != TelephonyManager.NETWORK_TYPE_UNKNOWN) { 267 // If IMS is registered over WWAN but WWAN PS is not in service, 268 // fallback to WWAN CS RAT 269 boolean isImsVoiceRatValid = 270 (imsVoiceRat == TelephonyManager.NETWORK_TYPE_IWLAN 271 || getRat(state, NetworkRegistrationInfo.DOMAIN_PS) 272 != TelephonyManager.NETWORK_TYPE_UNKNOWN); 273 if (isImsVoiceRatValid) { 274 return imsVoiceRat; 275 } 276 } 277 } 278 if (bearer == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) { 279 return TelephonyManager.NETWORK_TYPE_UNKNOWN; 280 } else { 281 return getRat(state, NetworkRegistrationInfo.DOMAIN_CS); 282 } 283 } 284 285 /** Returns RAT used by WWAN if WWAN is in service. */ getRat( ServiceState state, @NetworkRegistrationInfo.Domain int domain)286 public static @NetworkType int getRat( 287 ServiceState state, @NetworkRegistrationInfo.Domain int domain) { 288 final NetworkRegistrationInfo wwanRegInfo = 289 state.getNetworkRegistrationInfo( 290 domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 291 return wwanRegInfo != null && wwanRegInfo.isInService() 292 ? wwanRegInfo.getAccessNetworkTechnology() 293 : TelephonyManager.NETWORK_TYPE_UNKNOWN; 294 } 295 isEmergencyOnly(ServiceState state)296 private static boolean isEmergencyOnly(ServiceState state) { 297 NetworkRegistrationInfo regInfo = 298 state.getNetworkRegistrationInfo( 299 NetworkRegistrationInfo.DOMAIN_CS, 300 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 301 return regInfo != null && !regInfo.isInService() && regInfo.isEmergencyEnabled(); 302 } 303 isEndc(ServiceState state)304 private static boolean isEndc(ServiceState state) { 305 if (getRat(state, NetworkRegistrationInfo.DOMAIN_PS) != TelephonyManager.NETWORK_TYPE_LTE) { 306 return false; 307 } 308 int nrState = state.getNrState(); 309 return nrState == NetworkRegistrationInfo.NR_STATE_CONNECTED 310 || nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED; 311 } 312 313 @VisibleForTesting getTimeMillis()314 protected long getTimeMillis() { 315 return SystemClock.elapsedRealtime(); 316 } 317 318 private static final class TimestampedServiceState { 319 private final CellularServiceState mServiceState; 320 private final long mTimestamp; // Start time of the service state segment 321 TimestampedServiceState(CellularServiceState serviceState, long timestamp)322 TimestampedServiceState(CellularServiceState serviceState, long timestamp) { 323 mServiceState = serviceState; 324 mTimestamp = timestamp; 325 } 326 } 327 } 328