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 com.android.internal.annotations.VisibleForTesting; 29 import com.android.internal.telephony.Phone; 30 import com.android.internal.telephony.PhoneFactory; 31 import com.android.internal.telephony.ServiceStateTracker; 32 import com.android.internal.telephony.imsphone.ImsPhone; 33 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch; 34 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState; 35 import com.android.telephony.Rlog; 36 37 import java.util.concurrent.atomic.AtomicReference; 38 39 /** Tracks service state duration and switch metrics for each phone. */ 40 public class ServiceStateStats { 41 private static final String TAG = ServiceStateStats.class.getSimpleName(); 42 43 private final AtomicReference<TimestampedServiceState> mLastState = 44 new AtomicReference<>(new TimestampedServiceState(null, 0L)); 45 private final Phone mPhone; 46 private final PersistAtomsStorage mStorage; 47 ServiceStateStats(Phone phone)48 public ServiceStateStats(Phone phone) { 49 mPhone = phone; 50 mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage(); 51 } 52 53 /** Finalizes the durations of the current service state segment. */ conclude()54 public void conclude() { 55 final long now = getTimeMillis(); 56 TimestampedServiceState lastState = 57 mLastState.getAndUpdate( 58 state -> new TimestampedServiceState(state.mServiceState, now)); 59 addServiceState(lastState, now); 60 } 61 62 /** Updates service state when IMS voice registration changes. */ onImsVoiceRegistrationChanged()63 public void onImsVoiceRegistrationChanged() { 64 final long now = getTimeMillis(); 65 TimestampedServiceState lastState = 66 mLastState.getAndUpdate( 67 state -> { 68 if (state.mServiceState == null) { 69 return new TimestampedServiceState(null, now); 70 } 71 CellularServiceState newServiceState = copyOf(state.mServiceState); 72 newServiceState.voiceRat = 73 getVoiceRat(mPhone, getServiceStateForPhone(mPhone)); 74 return new TimestampedServiceState(newServiceState, now); 75 }); 76 addServiceState(lastState, now); 77 } 78 79 /** Updates the current service state. */ onServiceStateChanged(ServiceState serviceState)80 public void onServiceStateChanged(ServiceState serviceState) { 81 final long now = getTimeMillis(); 82 if (isModemOff(serviceState)) { 83 // Finish the duration of last service state and mark modem off 84 addServiceState(mLastState.getAndSet(new TimestampedServiceState(null, now)), now); 85 } else { 86 CellularServiceState newState = new CellularServiceState(); 87 newState.voiceRat = getVoiceRat(mPhone, serviceState); 88 newState.dataRat = getDataRat(serviceState); 89 newState.voiceRoamingType = serviceState.getVoiceRoamingType(); 90 newState.dataRoamingType = serviceState.getDataRoamingType(); 91 newState.isEndc = isEndc(serviceState); 92 newState.simSlotIndex = mPhone.getPhoneId(); 93 newState.isMultiSim = SimSlotState.isMultiSim(); 94 newState.carrierId = mPhone.getCarrierId(); 95 96 TimestampedServiceState prevState = 97 mLastState.getAndSet(new TimestampedServiceState(newState, now)); 98 addServiceStateAndSwitch( 99 prevState, now, getDataServiceSwitch(prevState.mServiceState, newState)); 100 } 101 } 102 addServiceState(TimestampedServiceState prevState, long now)103 private void addServiceState(TimestampedServiceState prevState, long now) { 104 addServiceStateAndSwitch(prevState, now, null); 105 } 106 addServiceStateAndSwitch( TimestampedServiceState prevState, long now, @Nullable CellularDataServiceSwitch serviceSwitch)107 private void addServiceStateAndSwitch( 108 TimestampedServiceState prevState, 109 long now, 110 @Nullable CellularDataServiceSwitch serviceSwitch) { 111 if (prevState.mServiceState == null) { 112 // Skip duration when modem is off 113 return; 114 } 115 if (now >= prevState.mTimestamp) { 116 CellularServiceState state = copyOf(prevState.mServiceState); 117 state.totalTimeMillis = now - prevState.mTimestamp; 118 mStorage.addCellularServiceStateAndCellularDataServiceSwitch(state, serviceSwitch); 119 } else { 120 Rlog.e(TAG, "addServiceState: durationMillis<0"); 121 } 122 } 123 124 @Nullable getDataServiceSwitch( @ullable CellularServiceState prevState, CellularServiceState nextState)125 private CellularDataServiceSwitch getDataServiceSwitch( 126 @Nullable CellularServiceState prevState, CellularServiceState nextState) { 127 // Record switch only if multi-SIM state and carrier ID are the same and data RAT differs. 128 if (prevState != null 129 && prevState.isMultiSim == nextState.isMultiSim 130 && prevState.carrierId == nextState.carrierId 131 && prevState.dataRat != nextState.dataRat) { 132 CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch(); 133 serviceSwitch.ratFrom = prevState.dataRat; 134 serviceSwitch.ratTo = nextState.dataRat; 135 serviceSwitch.isMultiSim = nextState.isMultiSim; 136 serviceSwitch.simSlotIndex = nextState.simSlotIndex; 137 serviceSwitch.carrierId = nextState.carrierId; 138 serviceSwitch.switchCount = 1; 139 return serviceSwitch; 140 } else { 141 return null; 142 } 143 } 144 145 /** Returns the service state for the given phone, or {@code null} if it cannot be obtained. */ 146 @Nullable getServiceStateForPhone(Phone phone)147 private static ServiceState getServiceStateForPhone(Phone phone) { 148 ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker(); 149 return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null; 150 } 151 152 /** 153 * Returns the band used from the given phone, or {@code 0} if it is invalid or cannot be 154 * determined. 155 */ getBand(Phone phone)156 static int getBand(Phone phone) { 157 ServiceState serviceState = getServiceStateForPhone(phone); 158 return getBand(serviceState); 159 } 160 161 /** 162 * Returns the band used from the given service state, or {@code 0} if it is invalid or cannot 163 * be determined. 164 */ getBand(@ullable ServiceState serviceState)165 static int getBand(@Nullable ServiceState serviceState) { 166 if (serviceState == null) { 167 Rlog.w(TAG, "getBand: serviceState=null"); 168 return 0; // Band unknown 169 } 170 int chNumber = serviceState.getChannelNumber(); 171 int band; 172 @NetworkType int rat = getRat(serviceState); 173 switch (rat) { 174 case TelephonyManager.NETWORK_TYPE_GSM: 175 case TelephonyManager.NETWORK_TYPE_GPRS: 176 case TelephonyManager.NETWORK_TYPE_EDGE: 177 band = AccessNetworkUtils.getOperatingBandForArfcn(chNumber); 178 break; 179 case TelephonyManager.NETWORK_TYPE_UMTS: 180 case TelephonyManager.NETWORK_TYPE_HSDPA: 181 case TelephonyManager.NETWORK_TYPE_HSUPA: 182 case TelephonyManager.NETWORK_TYPE_HSPA: 183 case TelephonyManager.NETWORK_TYPE_HSPAP: 184 band = AccessNetworkUtils.getOperatingBandForUarfcn(chNumber); 185 break; 186 case TelephonyManager.NETWORK_TYPE_LTE: 187 case TelephonyManager.NETWORK_TYPE_LTE_CA: 188 band = AccessNetworkUtils.getOperatingBandForEarfcn(chNumber); 189 break; 190 default: 191 Rlog.w(TAG, "getBand: unknown WWAN RAT " + rat); 192 band = 0; 193 break; 194 } 195 if (band == AccessNetworkUtils.INVALID_BAND) { 196 Rlog.w(TAG, "getBand: band invalid for rat=" + rat + " ch=" + chNumber); 197 return 0; 198 } else { 199 return band; 200 } 201 } 202 copyOf(CellularServiceState state)203 private static CellularServiceState copyOf(CellularServiceState state) { 204 // MessageNano does not support clone, have to copy manually 205 CellularServiceState copy = new CellularServiceState(); 206 copy.voiceRat = state.voiceRat; 207 copy.dataRat = state.dataRat; 208 copy.voiceRoamingType = state.voiceRoamingType; 209 copy.dataRoamingType = state.dataRoamingType; 210 copy.isEndc = state.isEndc; 211 copy.simSlotIndex = state.simSlotIndex; 212 copy.isMultiSim = state.isMultiSim; 213 copy.carrierId = state.carrierId; 214 copy.totalTimeMillis = state.totalTimeMillis; 215 return copy; 216 } 217 218 /** 219 * Returns {@code true} if modem radio is turned off (e.g. airplane mode). 220 * 221 * <p>Currently this is approximated by voice service state being {@code STATE_POWER_OFF}. 222 */ isModemOff(ServiceState state)223 private static boolean isModemOff(ServiceState state) { 224 // TODO(b/189335473): we should get this info from phone's radio power state, which is 225 // updated separately 226 return state.getVoiceRegState() == ServiceState.STATE_POWER_OFF; 227 } 228 229 /** 230 * Returns the current voice RAT from IMS registration if present, otherwise from the service 231 * state. 232 */ getVoiceRat(Phone phone, @Nullable ServiceState state)233 static @NetworkType int getVoiceRat(Phone phone, @Nullable ServiceState state) { 234 if (state == null) { 235 return TelephonyManager.NETWORK_TYPE_UNKNOWN; 236 } 237 ImsPhone imsPhone = (ImsPhone) phone.getImsPhone(); 238 if (imsPhone != null) { 239 @NetworkType int imsVoiceRat = imsPhone.getImsStats().getImsVoiceRadioTech(); 240 if (imsVoiceRat != TelephonyManager.NETWORK_TYPE_UNKNOWN) { 241 return imsVoiceRat; 242 } 243 } 244 return state.getVoiceNetworkType(); 245 } 246 247 /** 248 * Returns RAT used by WWAN. 249 * 250 * <p>Returns PS WWAN RAT, or CS WWAN RAT if PS WWAN RAT is unavailable. 251 */ getRat(ServiceState state)252 private static @NetworkType int getRat(ServiceState state) { 253 @NetworkType int rat = getDataRat(state); 254 if (rat == TelephonyManager.NETWORK_TYPE_UNKNOWN) { 255 rat = state.getVoiceNetworkType(); 256 } 257 return rat; 258 } 259 260 /** Returns PS (data) RAT used by WWAN. */ getDataRat(ServiceState state)261 static @NetworkType int getDataRat(ServiceState state) { 262 final NetworkRegistrationInfo wwanRegInfo = 263 state.getNetworkRegistrationInfo( 264 NetworkRegistrationInfo.DOMAIN_PS, 265 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 266 return wwanRegInfo != null 267 ? wwanRegInfo.getAccessNetworkTechnology() 268 : TelephonyManager.NETWORK_TYPE_UNKNOWN; 269 } 270 isEndc(ServiceState state)271 private static boolean isEndc(ServiceState state) { 272 if (getDataRat(state) != TelephonyManager.NETWORK_TYPE_LTE) { 273 return false; 274 } 275 int nrState = state.getNrState(); 276 return nrState == NetworkRegistrationInfo.NR_STATE_CONNECTED 277 || nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED; 278 } 279 280 @VisibleForTesting getTimeMillis()281 protected long getTimeMillis() { 282 return SystemClock.elapsedRealtime(); 283 } 284 285 private static final class TimestampedServiceState { 286 private final CellularServiceState mServiceState; 287 private final long mTimestamp; // Start time of the service state segment 288 TimestampedServiceState(CellularServiceState serviceState, long timestamp)289 TimestampedServiceState(CellularServiceState serviceState, long timestamp) { 290 mServiceState = serviceState; 291 mTimestamp = timestamp; 292 } 293 } 294 } 295