• 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 com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_HANDOVER;
20 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_NORMAL;
21 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_RADIO_OFF;
22 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
23 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;
24 
25 import android.annotation.Nullable;
26 import android.os.SystemClock;
27 import android.telephony.Annotation.ApnType;
28 import android.telephony.Annotation.DataFailureCause;
29 import android.telephony.Annotation.NetworkType;
30 import android.telephony.DataFailCause;
31 import android.telephony.ServiceState;
32 import android.telephony.ServiceState.RilRadioTechnology;
33 import android.telephony.TelephonyManager;
34 import android.telephony.data.ApnSetting.ProtocolType;
35 import android.telephony.data.DataCallResponse;
36 import android.telephony.data.DataService;
37 import android.telephony.data.DataService.DeactivateDataReason;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.telephony.Phone;
41 import com.android.internal.telephony.PhoneFactory;
42 import com.android.internal.telephony.ServiceStateTracker;
43 import com.android.internal.telephony.SubscriptionController;
44 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
45 import com.android.telephony.Rlog;
46 
47 import java.util.Random;
48 
49 /** Collects data call change events per DataConnection for the pulled atom. */
50 public class DataCallSessionStats {
51     private static final String TAG = DataCallSessionStats.class.getSimpleName();
52 
53     private final Phone mPhone;
54     private long mStartTime;
55     @Nullable private DataCallSession mDataCallSession;
56 
57     private final PersistAtomsStorage mAtomsStorage =
58             PhoneFactory.getMetricsCollector().getAtomsStorage();
59 
60     private static final Random RANDOM = new Random();
61 
DataCallSessionStats(Phone phone)62     public DataCallSessionStats(Phone phone) {
63         mPhone = phone;
64     }
65 
66     /** Creates a new ongoing atom when data call is set up. */
onSetupDataCall(@pnType int apnTypeBitMask)67     public synchronized void onSetupDataCall(@ApnType int apnTypeBitMask) {
68         mDataCallSession = getDefaultProto(apnTypeBitMask);
69         mStartTime = getTimeMillis();
70     }
71 
72     /**
73      * Updates the ongoing dataCall's atom for data call response event.
74      *
75      * @param response setup Data call response
76      * @param radioTechnology The data call RAT
77      * @param apnTypeBitmask APN type bitmask
78      * @param protocol Data connection protocol
79      * @param failureCause failure cause as per android.telephony.DataFailCause
80      */
onSetupDataCallResponse( @ullable DataCallResponse response, @RilRadioTechnology int radioTechnology, @ApnType int apnTypeBitmask, @ProtocolType int protocol, @DataFailureCause int failureCause)81     public synchronized void onSetupDataCallResponse(
82             @Nullable DataCallResponse response,
83             @RilRadioTechnology int radioTechnology,
84             @ApnType int apnTypeBitmask,
85             @ProtocolType int protocol,
86             @DataFailureCause int failureCause) {
87         // there should've been a call to onSetupDataCall to initiate the atom,
88         // so this method is being called out of order -> no metric will be logged
89         if (mDataCallSession == null) {
90             loge("onSetupDataCallResponse: no DataCallSession atom has been initiated.");
91             return;
92         }
93         mDataCallSession.ratAtEnd = ServiceState.rilRadioTechnologyToNetworkType(radioTechnology);
94 
95         // only set if apn hasn't been set during setup
96         if (mDataCallSession.apnTypeBitmask == 0) {
97             mDataCallSession.apnTypeBitmask = apnTypeBitmask;
98         }
99 
100         mDataCallSession.ipType = protocol;
101         mDataCallSession.failureCause = failureCause;
102         if (response != null) {
103             mDataCallSession.suggestedRetryMillis =
104                     (int) Math.min(response.getRetryDurationMillis(), Integer.MAX_VALUE);
105             // If setup has failed, then store the atom
106             if (failureCause != DataFailCause.NONE) {
107                 mDataCallSession.failureCause = failureCause;
108                 mDataCallSession.setupFailed = true;
109                 mDataCallSession.ongoing = false;
110                 mAtomsStorage.addDataCallSession(mDataCallSession);
111                 mDataCallSession = null;
112             }
113         }
114     }
115 
116     /**
117      * Updates the dataCall atom when data call is deactivated.
118      *
119      * @param reason Deactivate reason
120      */
setDeactivateDataCallReason(@eactivateDataReason int reason)121     public synchronized void setDeactivateDataCallReason(@DeactivateDataReason int reason) {
122         // there should've been another call to initiate the atom,
123         // so this method is being called out of order -> no metric will be logged
124         if (mDataCallSession == null) {
125             loge("setDeactivateDataCallReason: no DataCallSession atom has been initiated.");
126             return;
127         }
128         switch (reason) {
129             case DataService.REQUEST_REASON_NORMAL:
130                 mDataCallSession.deactivateReason =
131                         DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_NORMAL;
132                 break;
133             case DataService.REQUEST_REASON_SHUTDOWN:
134                 mDataCallSession.deactivateReason =
135                         DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_RADIO_OFF;
136                 break;
137             case DataService.REQUEST_REASON_HANDOVER:
138                 mDataCallSession.deactivateReason =
139                         DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_HANDOVER;
140                 break;
141             default:
142                 mDataCallSession.deactivateReason =
143                         DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
144                 break;
145         }
146     }
147 
148     /** Stores the atom when DataConnection reaches DISCONNECTED state.
149      *  @param failureCause failure cause as per android.telephony.DataFailCause
150      **/
onDataCallDisconnected(@ataFailureCause int failureCause)151     public synchronized void onDataCallDisconnected(@DataFailureCause int failureCause) {
152         // there should've been another call to initiate the atom,
153         // so this method is being called out of order -> no atom will be saved
154         if (mDataCallSession == null) {
155             loge("onDataCallDisconnected: no DataCallSession atom has been initiated.");
156             return;
157         }
158         mDataCallSession.failureCause = failureCause;
159         mDataCallSession.oosAtEnd = getIsOos();
160         mDataCallSession.ongoing = false;
161         mDataCallSession.durationMinutes = convertMillisToMinutes(getTimeMillis() - mStartTime);
162         // store for the data call list event, after DataCall is disconnected and entered into
163         // inactive mode
164         mAtomsStorage.addDataCallSession(mDataCallSession);
165         mDataCallSession = null;
166     }
167 
168     /**
169      * Updates the atom when data registration state or RAT changes.
170      *
171      * <p>NOTE: in {@link ServiceStateTracker}, change of channel number will trigger data
172      * registration state change.
173      */
onDrsOrRatChanged(@ilRadioTechnology int radioTechnology)174     public synchronized void onDrsOrRatChanged(@RilRadioTechnology int radioTechnology) {
175         @NetworkType int currentRat =
176                 ServiceState.rilRadioTechnologyToNetworkType(radioTechnology);
177         if (mDataCallSession != null
178                 && currentRat != TelephonyManager.NETWORK_TYPE_UNKNOWN
179                 && mDataCallSession.ratAtEnd != currentRat) {
180             mDataCallSession.ratSwitchCount++;
181             mDataCallSession.ratAtEnd = currentRat;
182             mDataCallSession.bandAtEnd = ServiceStateStats.getBand(mPhone, currentRat);
183         }
184     }
185 
convertMillisToMinutes(long millis)186     private static long convertMillisToMinutes(long millis) {
187         return Math.round(millis / 60000.0);
188     }
189 
190     /** Creates a proto for a normal {@code DataCallSession} with default values. */
getDefaultProto(@pnType int apnTypeBitmask)191     private DataCallSession getDefaultProto(@ApnType int apnTypeBitmask) {
192         DataCallSession proto = new DataCallSession();
193         proto.dimension = RANDOM.nextInt();
194         proto.isMultiSim = SimSlotState.isMultiSim();
195         proto.isEsim = SimSlotState.isEsim(mPhone.getPhoneId());
196         proto.apnTypeBitmask = apnTypeBitmask;
197         proto.carrierId = mPhone.getCarrierId();
198         proto.isRoaming = getIsRoaming();
199         proto.oosAtEnd = false;
200         proto.ratSwitchCount = 0L;
201         proto.isOpportunistic = getIsOpportunistic();
202         proto.ipType = DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;
203         proto.setupFailed = false;
204         proto.failureCause = DataFailCause.NONE;
205         proto.suggestedRetryMillis = 0;
206         proto.deactivateReason = DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
207         proto.durationMinutes = 0;
208         proto.ongoing = true;
209         return proto;
210     }
211 
getIsRoaming()212     private boolean getIsRoaming() {
213         ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker();
214         ServiceState serviceState =
215                 serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
216         return serviceState != null ? serviceState.getRoaming() : false;
217     }
218 
getIsOpportunistic()219     private boolean getIsOpportunistic() {
220         SubscriptionController subController = SubscriptionController.getInstance();
221         return subController != null ? subController.isOpportunistic(mPhone.getSubId()) : false;
222     }
223 
getIsOos()224     private boolean getIsOos() {
225         ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker();
226         ServiceState serviceState =
227                 serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
228         return serviceState != null
229                 ? serviceState.getDataRegistrationState() == ServiceState.STATE_OUT_OF_SERVICE
230                 : false;
231     }
232 
loge(String format, Object... args)233     private void loge(String format, Object... args) {
234         Rlog.e(TAG, "[" + mPhone.getPhoneId() + "]" + String.format(format, args));
235     }
236 
237     @VisibleForTesting
getTimeMillis()238     protected long getTimeMillis() {
239         return SystemClock.elapsedRealtime();
240     }
241 }
242