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.NonNull; 20 import android.annotation.Nullable; 21 import android.net.NetworkCapabilities; 22 import android.os.Bundle; 23 import android.os.Handler; 24 import android.os.HandlerThread; 25 import android.telephony.AccessNetworkConstants; 26 import android.telephony.Annotation.NetworkType; 27 import android.telephony.CellSignalStrength; 28 import android.telephony.NetworkRegistrationInfo; 29 import android.telephony.ServiceState; 30 import android.telephony.TelephonyManager; 31 import android.telephony.data.DataCallResponse; 32 import android.telephony.data.DataCallResponse.LinkStatus; 33 34 import com.android.internal.telephony.Phone; 35 import com.android.internal.telephony.PhoneFactory; 36 import com.android.internal.telephony.TelephonyStatsLog; 37 import com.android.internal.telephony.data.DataNetwork; 38 import com.android.internal.telephony.data.DataNetworkController; 39 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback; 40 import com.android.internal.telephony.data.DataStallRecoveryManager; 41 import com.android.internal.telephony.subscription.SubscriptionInfoInternal; 42 import com.android.internal.telephony.subscription.SubscriptionManagerService; 43 import com.android.telephony.Rlog; 44 45 import java.util.List; 46 47 /** 48 * Generates metrics related to data stall recovery events per phone ID for the pushed atom. 49 */ 50 public class DataStallRecoveryStats { 51 52 /** 53 * Value indicating that link bandwidth is unspecified. 54 * Copied from {@code NetworkCapabilities#LINK_BANDWIDTH_UNSPECIFIED} 55 */ 56 private static final int LINK_BANDWIDTH_UNSPECIFIED = 0; 57 58 private static final String TAG = "DSRS-"; 59 60 // Handler to upload metrics. 61 private final @NonNull Handler mHandler; 62 63 private final @NonNull String mTag; 64 private final @NonNull Phone mPhone; 65 66 // The interface name of the internet network. 67 private @Nullable String mIfaceName = null; 68 69 /* Metrics and stats data variables */ 70 private int mPhoneId = 0; 71 private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; 72 private int mSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 73 private int mBand = 0; 74 // The RAT used for data (including IWLAN). 75 private @NetworkType int mRat = TelephonyManager.NETWORK_TYPE_UNKNOWN; 76 private boolean mIsOpportunistic = false; 77 private boolean mIsMultiSim = false; 78 private int mNetworkRegState = NetworkRegistrationInfo 79 .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; 80 // Info of the other device in case of DSDS 81 private int mOtherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 82 private int mOtherNetworkRegState = NetworkRegistrationInfo 83 .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; 84 // Link status of the data network 85 private @LinkStatus int mInternetLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN; 86 87 // The link bandwidth of the data network 88 private int mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; 89 private int mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; 90 91 /** 92 * Constructs a new instance of {@link DataStallRecoveryStats}. 93 */ DataStallRecoveryStats(@onNull final Phone phone, @NonNull final DataNetworkController dataNetworkController)94 public DataStallRecoveryStats(@NonNull final Phone phone, 95 @NonNull final DataNetworkController dataNetworkController) { 96 mTag = TAG + phone.getPhoneId(); 97 mPhone = phone; 98 99 HandlerThread handlerThread = new HandlerThread(mTag + "-thread"); 100 handlerThread.start(); 101 mHandler = new Handler(handlerThread.getLooper()); 102 103 dataNetworkController.registerDataNetworkControllerCallback( 104 new DataNetworkControllerCallback(mHandler::post) { 105 @Override 106 public void onInternetDataNetworkConnected( 107 @NonNull List<DataNetwork> internetNetworks) { 108 for (DataNetwork dataNetwork : internetNetworks) { 109 mIfaceName = dataNetwork.getLinkProperties().getInterfaceName(); 110 break; 111 } 112 } 113 114 @Override 115 public void onInternetDataNetworkDisconnected() { 116 mIfaceName = null; 117 } 118 119 @Override 120 public void onPhysicalLinkStatusChanged(@LinkStatus int status) { 121 mInternetLinkStatus = status; 122 } 123 }); 124 } 125 126 /** 127 * Create and push new atom when there is a data stall recovery event. 128 * 129 * @param action The recovery action. 130 * @param isRecovered Whether the data stall has been recovered. 131 * @param duration The duration from data stall occurred in milliseconds. 132 * @param reason The reason for the recovery. 133 * @param isFirstValidation Whether this is the first validation after recovery. 134 * @param durationOfAction The duration of the current action in milliseconds. 135 */ uploadMetrics( @ataStallRecoveryManager.RecoveryAction int action, boolean isRecovered, int duration, @DataStallRecoveryManager.RecoveredReason int reason, boolean isFirstValidation, int durationOfAction)136 public void uploadMetrics( 137 @DataStallRecoveryManager.RecoveryAction int action, 138 boolean isRecovered, 139 int duration, 140 @DataStallRecoveryManager.RecoveredReason int reason, 141 boolean isFirstValidation, 142 int durationOfAction) { 143 144 mHandler.post(() -> { 145 // Update data stall stats 146 log("Set recovery action to " + action); 147 148 // Refreshes the metrics data. 149 try { 150 refreshMetricsData(); 151 } catch (Exception e) { 152 loge("The metrics data cannot be refreshed.", e); 153 return; 154 } 155 156 TelephonyStatsLog.write( 157 TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED, 158 mCarrierId, 159 mRat, 160 mSignalStrength, 161 action, 162 mIsOpportunistic, 163 mIsMultiSim, 164 mBand, 165 isRecovered, 166 duration, 167 reason, 168 mOtherSignalStrength, 169 mOtherNetworkRegState, 170 mNetworkRegState, 171 isFirstValidation, 172 mPhoneId, 173 durationOfAction, 174 mInternetLinkStatus, 175 mLinkUpBandwidthKbps, 176 mLinkDownBandwidthKbps); 177 178 log("Upload stats: " 179 + "Action:" 180 + action 181 + ", Recovered:" 182 + isRecovered 183 + ", Duration:" 184 + duration 185 + ", Reason:" 186 + reason 187 + ", First validation:" 188 + isFirstValidation 189 + ", Duration of action:" 190 + durationOfAction 191 + ", " 192 + this); 193 }); 194 } 195 196 /** 197 * Refreshes the metrics data. 198 */ refreshMetricsData()199 private void refreshMetricsData() { 200 logd("Refreshes the metrics data."); 201 // Update phone id/carrier id and signal strength 202 mPhoneId = mPhone.getPhoneId() + 1; 203 mCarrierId = mPhone.getCarrierId(); 204 mSignalStrength = mPhone.getSignalStrength().getLevel(); 205 206 // Update the bandwidth. 207 updateBandwidths(); 208 209 // Update the RAT and band. 210 updateRatAndBand(); 211 212 // Update the opportunistic state. 213 mIsOpportunistic = getIsOpportunistic(mPhone); 214 215 // Update the multi-SIM state. 216 mIsMultiSim = SimSlotState.getCurrentState().numActiveSims > 1; 217 218 // Update the network registration state. 219 updateNetworkRegState(); 220 221 // Update the DSDS information. 222 updateDsdsInfo(); 223 } 224 225 /** 226 * Updates the bandwidth for the current data network. 227 */ updateBandwidths()228 private void updateBandwidths() { 229 mLinkDownBandwidthKbps = mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; 230 231 if (mIfaceName == null) { 232 loge("Interface name is null"); 233 return; 234 } 235 236 DataNetworkController dataNetworkController = mPhone.getDataNetworkController(); 237 if (dataNetworkController == null) { 238 loge("DataNetworkController is null"); 239 return; 240 } 241 242 DataNetwork dataNetwork = dataNetworkController.getDataNetworkByInterface(mIfaceName); 243 if (dataNetwork == null) { 244 loge("DataNetwork is null"); 245 return; 246 } 247 NetworkCapabilities networkCapabilities = dataNetwork.getNetworkCapabilities(); 248 if (networkCapabilities == null) { 249 loge("NetworkCapabilities is null"); 250 return; 251 } 252 253 mLinkDownBandwidthKbps = networkCapabilities.getLinkDownstreamBandwidthKbps(); 254 mLinkUpBandwidthKbps = networkCapabilities.getLinkUpstreamBandwidthKbps(); 255 } 256 updateRatAndBand()257 private void updateRatAndBand() { 258 mRat = TelephonyManager.NETWORK_TYPE_UNKNOWN; 259 mBand = 0; 260 ServiceState serviceState = mPhone.getServiceState(); 261 if (serviceState == null) { 262 loge("ServiceState is null"); 263 return; 264 } 265 266 mRat = serviceState.getDataNetworkType(); 267 mBand = 268 (mRat == TelephonyManager.NETWORK_TYPE_IWLAN) ? 0 : ServiceStateStats.getBand(mPhone); 269 } 270 getIsOpportunistic(@onNull Phone phone)271 private static boolean getIsOpportunistic(@NonNull Phone phone) { 272 SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance() 273 .getSubscriptionInfoInternal(phone.getSubId()); 274 return subInfo != null && subInfo.isOpportunistic(); 275 } 276 updateNetworkRegState()277 private void updateNetworkRegState() { 278 mNetworkRegState = NetworkRegistrationInfo 279 .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; 280 281 NetworkRegistrationInfo phoneRegInfo = mPhone.getServiceState() 282 .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, 283 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 284 if (phoneRegInfo != null) { 285 mNetworkRegState = phoneRegInfo.getRegistrationState(); 286 } 287 } 288 updateDsdsInfo()289 private void updateDsdsInfo() { 290 mOtherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 291 mOtherNetworkRegState = NetworkRegistrationInfo 292 .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; 293 for (Phone otherPhone : PhoneFactory.getPhones()) { 294 if (otherPhone.getPhoneId() == mPhone.getPhoneId()) continue; 295 if (!getIsOpportunistic(otherPhone)) { 296 mOtherSignalStrength = otherPhone.getSignalStrength().getLevel(); 297 NetworkRegistrationInfo regInfo = otherPhone.getServiceState() 298 .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, 299 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 300 if (regInfo != null) { 301 mOtherNetworkRegState = regInfo.getRegistrationState(); 302 } 303 break; 304 } 305 } 306 } 307 308 /** 309 * Return bundled data stall recovery metrics data. 310 * 311 * @param action The recovery action. 312 * @param isRecovered Whether the data stall has been recovered. 313 * @param duration The duration from data stall occurred in milliseconds. 314 * @param reason The reason for the recovery. 315 * @param isFirstValidation Whether this is the first validation after recovery. 316 * @param durationOfAction The duration of the current action in milliseconds. 317 */ getDataStallRecoveryMetricsData( @ataStallRecoveryManager.RecoveryAction int action, boolean isRecovered, int duration, @DataStallRecoveryManager.RecoveredReason int reason, boolean isFirstValidation, int durationOfAction)318 public Bundle getDataStallRecoveryMetricsData( 319 @DataStallRecoveryManager.RecoveryAction int action, 320 boolean isRecovered, 321 int duration, 322 @DataStallRecoveryManager.RecoveredReason int reason, 323 boolean isFirstValidation, 324 int durationOfAction) { 325 Bundle bundle = new Bundle(); 326 bundle.putInt("Action", action); 327 bundle.putBoolean("IsRecovered", isRecovered); 328 bundle.putInt("Duration", duration); 329 bundle.putInt("Reason", reason); 330 bundle.putBoolean("IsFirstValidation", isFirstValidation); 331 bundle.putInt("DurationOfAction", durationOfAction); 332 bundle.putInt("PhoneId", mPhoneId); 333 bundle.putInt("CarrierId", mCarrierId); 334 bundle.putInt("SignalStrength", mSignalStrength); 335 bundle.putInt("Band", mBand); 336 bundle.putInt("Rat", mRat); 337 bundle.putBoolean("IsOpportunistic", mIsOpportunistic); 338 bundle.putBoolean("IsMultiSim", mIsMultiSim); 339 bundle.putInt("NetworkRegState", mNetworkRegState); 340 bundle.putInt("OtherSignalStrength", mOtherSignalStrength); 341 bundle.putInt("OtherNetworkRegState", mOtherNetworkRegState); 342 bundle.putInt("InternetLinkStatus", mInternetLinkStatus); 343 bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps); 344 bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps); 345 return bundle; 346 } 347 log(@onNull String s)348 private void log(@NonNull String s) { 349 Rlog.i(mTag, s); 350 } 351 logd(@onNull String s)352 private void logd(@NonNull String s) { 353 Rlog.d(mTag, s); 354 } 355 loge(@onNull String s)356 private void loge(@NonNull String s) { 357 Rlog.e(mTag, s); 358 } 359 loge(@onNull String s, Throwable tr)360 private void loge(@NonNull String s, Throwable tr) { 361 Rlog.e(mTag, s, tr); 362 } 363 364 @Override toString()365 public String toString() { 366 return "DataStallRecoveryStats {" 367 + "Phone id:" 368 + mPhoneId 369 + ", Signal strength:" 370 + mSignalStrength 371 + ", Band:" + mBand 372 + ", RAT:" + mRat 373 + ", Opportunistic:" 374 + mIsOpportunistic 375 + ", Multi-SIM:" 376 + mIsMultiSim 377 + ", Network reg state:" 378 + mNetworkRegState 379 + ", Other signal strength:" 380 + mOtherSignalStrength 381 + ", Other network reg state:" 382 + mOtherNetworkRegState 383 + ", Link status:" 384 + mInternetLinkStatus 385 + ", Link down bandwidth:" 386 + mLinkDownBandwidthKbps 387 + ", Link up bandwidth:" 388 + mLinkUpBandwidthKbps 389 + "}"; 390 } 391 } 392