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