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 static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK; 20 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK; 21 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT; 22 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS; 23 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE; 24 import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS; 25 26 import android.annotation.ElapsedRealtimeLong; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.net.ConnectivityDiagnosticsManager; 30 import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback; 31 import android.net.ConnectivityDiagnosticsManager.ConnectivityReport; 32 import android.net.ConnectivityDiagnosticsManager.DataStallReport; 33 import android.net.NetworkCapabilities; 34 import android.net.NetworkRequest; 35 import android.os.Bundle; 36 import android.os.Handler; 37 import android.os.HandlerThread; 38 import android.os.PersistableBundle; 39 import android.os.SystemClock; 40 import android.telephony.AccessNetworkConstants; 41 import android.telephony.Annotation.NetworkType; 42 import android.telephony.CellSignalStrength; 43 import android.telephony.NetworkRegistrationInfo; 44 import android.telephony.ServiceState; 45 import android.telephony.TelephonyManager; 46 import android.telephony.data.DataCallResponse; 47 import android.telephony.data.DataCallResponse.LinkStatus; 48 import android.text.TextUtils; 49 50 import com.android.internal.telephony.Phone; 51 import com.android.internal.telephony.PhoneFactory; 52 import com.android.internal.telephony.TelephonyStatsLog; 53 import com.android.internal.telephony.data.DataNetwork; 54 import com.android.internal.telephony.data.DataNetworkController; 55 import com.android.internal.telephony.data.DataNetworkController.DataNetworkControllerCallback; 56 import com.android.internal.telephony.data.DataStallRecoveryManager; 57 import com.android.internal.telephony.flags.FeatureFlags; 58 import com.android.internal.telephony.subscription.SubscriptionInfoInternal; 59 import com.android.internal.telephony.subscription.SubscriptionManagerService; 60 import com.android.telephony.Rlog; 61 62 import java.util.Set; 63 import java.util.concurrent.Executor; 64 import java.util.concurrent.TimeUnit; 65 66 /** 67 * Generates metrics related to data stall recovery events per phone ID for the pushed atom. 68 */ 69 public class DataStallRecoveryStats { 70 71 /** 72 * Value indicating that link bandwidth is unspecified. 73 * Copied from {@code NetworkCapabilities#LINK_BANDWIDTH_UNSPECIFIED} 74 */ 75 private static final int LINK_BANDWIDTH_UNSPECIFIED = 0; 76 77 private static final String TAG = "DSRS-"; 78 79 private static final int UNSET_DIAGNOSTIC_STATE = -1; 80 81 private static final long REFRESH_DURATION_IN_MILLIS = TimeUnit.MINUTES.toMillis(3); 82 83 // Handler to upload metrics. 84 private final @NonNull Handler mHandler; 85 86 private final @NonNull String mTag; 87 private final @NonNull Phone mPhone; 88 private final @NonNull TelephonyManager mTelephonyManager; 89 private final @NonNull FeatureFlags mFeatureFlags; 90 91 // Flag to control the DSRS diagnostics 92 private final boolean mIsDsrsDiagnosticsEnabled; 93 94 // The interface name of the internet network. 95 private @Nullable String mIfaceName = null; 96 97 /* Metrics and stats data variables */ 98 // Record metrics refresh time in milliseconds to decide whether to refresh data again 99 @ElapsedRealtimeLong 100 private long mMetricsReflashTime = 0L; 101 private int mPhoneId = 0; 102 private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID; 103 private int mConvertedMccMnc = -1; 104 private int mSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 105 private int mBand = 0; 106 // The RAT used for data (including IWLAN). 107 private @NetworkType int mRat = TelephonyManager.NETWORK_TYPE_UNKNOWN; 108 private boolean mIsOpportunistic = false; 109 private boolean mIsMultiSim = false; 110 private int mNetworkRegState = NetworkRegistrationInfo 111 .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; 112 // Info of the other device in case of DSDS 113 private int mOtherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 114 private int mOtherNetworkRegState = NetworkRegistrationInfo 115 .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; 116 // Link status of the data network 117 private @LinkStatus int mInternetLinkStatus = DataCallResponse.LINK_STATUS_UNKNOWN; 118 119 // The link bandwidth of the data network 120 private int mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; 121 private int mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; 122 123 // Connectivity diagnostics states 124 private int mNetworkProbesResult = UNSET_DIAGNOSTIC_STATE; 125 private int mNetworkProbesType = UNSET_DIAGNOSTIC_STATE; 126 private int mNetworkValidationResult = UNSET_DIAGNOSTIC_STATE; 127 private int mTcpMetricsCollectionPeriodMillis = UNSET_DIAGNOSTIC_STATE; 128 private int mTcpPacketFailRate = UNSET_DIAGNOSTIC_STATE; 129 private int mDnsConsecutiveTimeouts = UNSET_DIAGNOSTIC_STATE; 130 131 private ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager = null; 132 private ConnectivityDiagnosticsCallback mConnectivityDiagnosticsCallback = null; 133 private static final Executor INLINE_EXECUTOR = x -> x.run(); 134 135 /** 136 * Constructs a new instance of {@link DataStallRecoveryStats}. 137 */ DataStallRecoveryStats( @onNull final Phone phone, @NonNull FeatureFlags featureFlags, @NonNull final DataNetworkController dataNetworkController)138 public DataStallRecoveryStats( 139 @NonNull final Phone phone, 140 @NonNull FeatureFlags featureFlags, 141 @NonNull final DataNetworkController dataNetworkController) { 142 mTag = TAG + phone.getPhoneId(); 143 mPhone = phone; 144 mFeatureFlags = featureFlags; 145 146 HandlerThread handlerThread = new HandlerThread(mTag + "-thread"); 147 handlerThread.start(); 148 mHandler = new Handler(handlerThread.getLooper()); 149 mTelephonyManager = mPhone.getContext().getSystemService(TelephonyManager.class); 150 151 dataNetworkController.registerDataNetworkControllerCallback( 152 new DataNetworkControllerCallback(mHandler::post) { 153 @Override 154 public void onConnectedInternetDataNetworksChanged( 155 @NonNull Set<DataNetwork> internetNetworks) { 156 mIfaceName = null; 157 for (DataNetwork dataNetwork : internetNetworks) { 158 mIfaceName = dataNetwork.getLinkProperties().getInterfaceName(); 159 break; 160 } 161 } 162 163 @Override 164 public void onPhysicalLinkStatusChanged(@LinkStatus int status) { 165 mInternetLinkStatus = status; 166 } 167 }); 168 169 mIsDsrsDiagnosticsEnabled = mFeatureFlags.dsrsDiagnosticsEnabled(); 170 if (mIsDsrsDiagnosticsEnabled) { 171 try { 172 // Register ConnectivityDiagnosticsCallback to get diagnostics states 173 mConnectivityDiagnosticsManager = 174 mPhone.getContext().getSystemService(ConnectivityDiagnosticsManager.class); 175 mConnectivityDiagnosticsCallback = new ConnectivityDiagnosticsCallback() { 176 @Override 177 public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) { 178 PersistableBundle bundle = report.getAdditionalInfo(); 179 mNetworkProbesResult = bundle.getInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK); 180 mNetworkProbesType = bundle.getInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK); 181 mNetworkValidationResult = bundle.getInt(KEY_NETWORK_VALIDATION_RESULT); 182 } 183 184 @Override 185 public void onDataStallSuspected(@NonNull DataStallReport report) { 186 PersistableBundle bundle = report.getStallDetails(); 187 mTcpMetricsCollectionPeriodMillis = 188 bundle.getInt(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS); 189 mTcpPacketFailRate = bundle.getInt(KEY_TCP_PACKET_FAIL_RATE); 190 mDnsConsecutiveTimeouts = bundle.getInt(KEY_DNS_CONSECUTIVE_TIMEOUTS); 191 } 192 }; 193 mConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback( 194 new NetworkRequest.Builder() 195 .clearCapabilities() 196 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 197 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 198 .build(), 199 INLINE_EXECUTOR, 200 mConnectivityDiagnosticsCallback 201 ); 202 } catch (Exception e) { 203 mConnectivityDiagnosticsManager = null; 204 mConnectivityDiagnosticsCallback = null; 205 } 206 } 207 } 208 209 /** 210 * Create and push new atom when there is a data stall recovery event. 211 * 212 * @param action The recovery action. 213 * @param isRecovered Whether the data stall has been recovered. 214 * @param duration The duration from data stall occurred in milliseconds. 215 * @param reason The reason for the recovery. 216 * @param isFirstValidation Whether this is the first validation after recovery. 217 * @param durationOfAction The duration of the current action in milliseconds. 218 */ uploadMetrics( @ataStallRecoveryManager.RecoveryAction int action, boolean isRecovered, int duration, @DataStallRecoveryManager.RecoveredReason int reason, boolean isFirstValidation, int durationOfAction)219 public void uploadMetrics( 220 @DataStallRecoveryManager.RecoveryAction int action, 221 boolean isRecovered, 222 int duration, 223 @DataStallRecoveryManager.RecoveredReason int reason, 224 boolean isFirstValidation, 225 int durationOfAction) { 226 227 mHandler.post(() -> { 228 // Update data stall stats 229 log("Set recovery action to " + action); 230 231 // Refreshes the metrics data. 232 try { 233 refreshMetricsData(); 234 } catch (Exception e) { 235 loge("The metrics data cannot be refreshed.", e); 236 return; 237 } 238 239 TelephonyStatsLog.write( 240 TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED, 241 mCarrierId, 242 mRat, 243 mSignalStrength, 244 action, 245 mIsOpportunistic, 246 mIsMultiSim, 247 mBand, 248 isRecovered, 249 duration, 250 reason, 251 mOtherSignalStrength, 252 mOtherNetworkRegState, 253 mNetworkRegState, 254 isFirstValidation, 255 mPhoneId, 256 durationOfAction, 257 mInternetLinkStatus, 258 mLinkUpBandwidthKbps, 259 mLinkDownBandwidthKbps); 260 261 log("Upload stats: " 262 + "Action:" 263 + action 264 + ", Recovered:" 265 + isRecovered 266 + ", Duration:" 267 + duration 268 + ", Reason:" 269 + reason 270 + ", First validation:" 271 + isFirstValidation 272 + ", Duration of action:" 273 + durationOfAction 274 + ", " 275 + this); 276 }); 277 } 278 279 /** 280 * Refreshes the metrics data. 281 */ refreshMetricsData()282 private void refreshMetricsData() { 283 logd("Refreshes the metrics data."); 284 // Update the metrics reflash time 285 mMetricsReflashTime = SystemClock.elapsedRealtime(); 286 // Update phone id/carrier id and signal strength 287 mPhoneId = mPhone.getPhoneId() + 1; 288 mCarrierId = mPhone.getCarrierId(); 289 mSignalStrength = mPhone.getSignalStrength().getLevel(); 290 if (mIsDsrsDiagnosticsEnabled) { 291 // Get the MCCMNC and convert it to an int 292 String networkOperator = mTelephonyManager.getNetworkOperator(); 293 if (!TextUtils.isEmpty(networkOperator)) { 294 try { 295 mConvertedMccMnc = Integer.parseInt(networkOperator); 296 } catch (NumberFormatException e) { 297 loge("Invalid MCCMNC format: " + networkOperator); 298 mConvertedMccMnc = -1; 299 } 300 } else { 301 mConvertedMccMnc = -1; 302 } 303 } 304 305 // Update the bandwidth. 306 updateBandwidths(); 307 308 // Update the RAT and band. 309 updateRatAndBand(); 310 311 // Update the opportunistic state. 312 mIsOpportunistic = getIsOpportunistic(mPhone); 313 314 // Update the multi-SIM state. 315 mIsMultiSim = SimSlotState.getCurrentState().numActiveSims > 1; 316 317 // Update the network registration state. 318 updateNetworkRegState(); 319 320 // Update the DSDS information. 321 updateDsdsInfo(); 322 } 323 324 /** 325 * Updates the bandwidth for the current data network. 326 */ updateBandwidths()327 private void updateBandwidths() { 328 mLinkDownBandwidthKbps = mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; 329 330 if (mIfaceName == null) { 331 loge("Interface name is null"); 332 return; 333 } 334 335 DataNetworkController dataNetworkController = mPhone.getDataNetworkController(); 336 if (dataNetworkController == null) { 337 loge("DataNetworkController is null"); 338 return; 339 } 340 341 DataNetwork dataNetwork = dataNetworkController.getDataNetworkByInterface(mIfaceName); 342 if (dataNetwork == null) { 343 loge("DataNetwork is null"); 344 return; 345 } 346 NetworkCapabilities networkCapabilities = dataNetwork.getNetworkCapabilities(); 347 if (networkCapabilities == null) { 348 loge("NetworkCapabilities is null"); 349 return; 350 } 351 352 mLinkDownBandwidthKbps = networkCapabilities.getLinkDownstreamBandwidthKbps(); 353 mLinkUpBandwidthKbps = networkCapabilities.getLinkUpstreamBandwidthKbps(); 354 } 355 updateRatAndBand()356 private void updateRatAndBand() { 357 mRat = TelephonyManager.NETWORK_TYPE_UNKNOWN; 358 mBand = 0; 359 ServiceState serviceState = mPhone.getServiceState(); 360 if (serviceState == null) { 361 loge("ServiceState is null"); 362 return; 363 } 364 365 mRat = serviceState.getDataNetworkType(); 366 mBand = 367 (mRat == TelephonyManager.NETWORK_TYPE_IWLAN) ? 0 : ServiceStateStats.getBand(mPhone); 368 } 369 getIsOpportunistic(@onNull Phone phone)370 private static boolean getIsOpportunistic(@NonNull Phone phone) { 371 SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance() 372 .getSubscriptionInfoInternal(phone.getSubId()); 373 return subInfo != null && subInfo.isOpportunistic(); 374 } 375 updateNetworkRegState()376 private void updateNetworkRegState() { 377 mNetworkRegState = NetworkRegistrationInfo 378 .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; 379 380 NetworkRegistrationInfo phoneRegInfo = mPhone.getServiceState() 381 .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, 382 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 383 if (phoneRegInfo != null) { 384 mNetworkRegState = phoneRegInfo.getRegistrationState(); 385 } 386 } 387 updateDsdsInfo()388 private void updateDsdsInfo() { 389 mOtherSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; 390 mOtherNetworkRegState = NetworkRegistrationInfo 391 .REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING; 392 for (Phone otherPhone : PhoneFactory.getPhones()) { 393 if (otherPhone.getPhoneId() == mPhone.getPhoneId()) continue; 394 if (!getIsOpportunistic(otherPhone)) { 395 mOtherSignalStrength = otherPhone.getSignalStrength().getLevel(); 396 NetworkRegistrationInfo regInfo = otherPhone.getServiceState() 397 .getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS, 398 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 399 if (regInfo != null) { 400 mOtherNetworkRegState = regInfo.getRegistrationState(); 401 } 402 break; 403 } 404 } 405 } 406 407 /** 408 * Return bundled data stall recovery metrics data. 409 * 410 * @param action The recovery action. 411 * @param isRecovered Whether the data stall has been recovered. 412 * @param duration The duration from data stall occurred in milliseconds. 413 * @param reason The reason for the recovery. 414 * @param validationCount The total number of validation duration a data stall. 415 * @param actionValidationCount The number of validation for current action during a data stall 416 * @param durationOfAction The duration of the current action in milliseconds. 417 */ getDataStallRecoveryMetricsData( @ataStallRecoveryManager.RecoveryAction int action, boolean isRecovered, int duration, @DataStallRecoveryManager.RecoveredReason int reason, int validationCount, int actionValidationCount, int durationOfAction)418 public Bundle getDataStallRecoveryMetricsData( 419 @DataStallRecoveryManager.RecoveryAction int action, 420 boolean isRecovered, 421 int duration, 422 @DataStallRecoveryManager.RecoveredReason int reason, 423 int validationCount, 424 int actionValidationCount, 425 int durationOfAction) { 426 427 if (mIsDsrsDiagnosticsEnabled) { 428 // Refresh data if the data has not been updated within 3 minutes 429 final long refreshDuration = SystemClock.elapsedRealtime() - mMetricsReflashTime; 430 if (refreshDuration > REFRESH_DURATION_IN_MILLIS) { 431 // Refreshes the metrics data. 432 try { 433 refreshMetricsData(); 434 } catch (Exception e) { 435 loge("The metrics data cannot be refreshed.", e); 436 } 437 } 438 } 439 440 Bundle bundle = new Bundle(); 441 442 if (mIsDsrsDiagnosticsEnabled) { 443 bundle.putInt("Action", action); 444 bundle.putInt("IsRecovered", isRecovered ? 1 : 0); 445 bundle.putInt("Duration", duration); 446 bundle.putInt("Reason", reason); 447 bundle.putInt("DurationOfAction", durationOfAction); 448 bundle.putInt("ValidationCount", validationCount); 449 bundle.putInt("ActionValidationCount", actionValidationCount); 450 bundle.putInt("PhoneId", mPhoneId); 451 bundle.putInt("CarrierId", mCarrierId); 452 bundle.putInt("MccMnc", mConvertedMccMnc); 453 bundle.putInt("SignalStrength", mSignalStrength); 454 bundle.putInt("Band", mBand); 455 bundle.putInt("Rat", mRat); 456 bundle.putInt("IsOpportunistic", mIsOpportunistic ? 1 : 0); 457 bundle.putInt("IsMultiSim", mIsMultiSim ? 1 : 0); 458 bundle.putInt("NetworkRegState", mNetworkRegState); 459 bundle.putInt("OtherSignalStrength", mOtherSignalStrength); 460 bundle.putInt("OtherNetworkRegState", mOtherNetworkRegState); 461 bundle.putInt("InternetLinkStatus", mInternetLinkStatus); 462 bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps); 463 bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps); 464 bundle.putInt("NetworkProbesResult", mNetworkProbesResult); 465 bundle.putInt("NetworkProbesType", mNetworkProbesType); 466 bundle.putInt("NetworkValidationResult", mNetworkValidationResult); 467 bundle.putInt("TcpMetricsCollectionPeriodMillis", mTcpMetricsCollectionPeriodMillis); 468 bundle.putInt("TcpPacketFailRate", mTcpPacketFailRate); 469 bundle.putInt("DnsConsecutiveTimeouts", mDnsConsecutiveTimeouts); 470 } else { 471 bundle.putInt("Action", action); 472 bundle.putBoolean("IsRecovered", isRecovered); 473 bundle.putInt("Duration", duration); 474 bundle.putInt("Reason", reason); 475 bundle.putBoolean("IsFirstValidation", validationCount == 1); 476 bundle.putInt("DurationOfAction", durationOfAction); 477 bundle.putInt("PhoneId", mPhoneId); 478 bundle.putInt("CarrierId", mCarrierId); 479 bundle.putInt("SignalStrength", mSignalStrength); 480 bundle.putInt("Band", mBand); 481 bundle.putInt("Rat", mRat); 482 bundle.putBoolean("IsOpportunistic", mIsOpportunistic); 483 bundle.putBoolean("IsMultiSim", mIsMultiSim); 484 bundle.putInt("NetworkRegState", mNetworkRegState); 485 bundle.putInt("OtherSignalStrength", mOtherSignalStrength); 486 bundle.putInt("OtherNetworkRegState", mOtherNetworkRegState); 487 bundle.putInt("InternetLinkStatus", mInternetLinkStatus); 488 bundle.putInt("LinkDownBandwidthKbps", mLinkDownBandwidthKbps); 489 bundle.putInt("LinkUpBandwidthKbps", mLinkUpBandwidthKbps); 490 } 491 492 return bundle; 493 } 494 log(@onNull String s)495 private void log(@NonNull String s) { 496 Rlog.i(mTag, s); 497 } 498 logd(@onNull String s)499 private void logd(@NonNull String s) { 500 Rlog.d(mTag, s); 501 } 502 loge(@onNull String s)503 private void loge(@NonNull String s) { 504 Rlog.e(mTag, s); 505 } 506 loge(@onNull String s, Throwable tr)507 private void loge(@NonNull String s, Throwable tr) { 508 Rlog.e(mTag, s, tr); 509 } 510 511 @Override toString()512 public String toString() { 513 return "DataStallRecoveryStats {" 514 + "Phone id:" 515 + mPhoneId 516 + ", Signal strength:" 517 + mSignalStrength 518 + ", Band:" + mBand 519 + ", RAT:" + mRat 520 + ", Opportunistic:" 521 + mIsOpportunistic 522 + ", Multi-SIM:" 523 + mIsMultiSim 524 + ", Network reg state:" 525 + mNetworkRegState 526 + ", Other signal strength:" 527 + mOtherSignalStrength 528 + ", Other network reg state:" 529 + mOtherNetworkRegState 530 + ", Link status:" 531 + mInternetLinkStatus 532 + ", Link down bandwidth:" 533 + mLinkDownBandwidthKbps 534 + ", Link up bandwidth:" 535 + mLinkUpBandwidthKbps 536 + "}"; 537 } 538 } 539