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