• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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