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.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
20 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
21 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN;
22 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_FIVE_MINUTES;
23 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_HOUR;
24 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_MINUTE;
25 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_TEN_MINUTES;
26 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_THIRTY_MINUTES;
27 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_MORE_THAN_ONE_HOUR;
28 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
29 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO;
30 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT;
31 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_FULLBAND;
32 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
33 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
34 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
35 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_WIDEBAND;
36 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT;
37 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
38 
39 import android.annotation.NonNull;
40 import android.annotation.Nullable;
41 import android.content.Context;
42 import android.net.wifi.WifiInfo;
43 import android.net.wifi.WifiManager;
44 import android.os.PersistableBundle;
45 import android.os.SystemClock;
46 import android.telecom.VideoProfile;
47 import android.telecom.VideoProfile.VideoState;
48 import android.telephony.Annotation.NetworkType;
49 import android.telephony.AnomalyReporter;
50 import android.telephony.CarrierConfigManager;
51 import android.telephony.DisconnectCause;
52 import android.telephony.NetworkRegistrationInfo;
53 import android.telephony.PreciseDataConnectionState;
54 import android.telephony.ServiceState;
55 import android.telephony.TelephonyManager;
56 import android.telephony.TelephonyManager.CallComposerStatus;
57 import android.telephony.data.ApnSetting;
58 import android.telephony.ims.ImsReasonInfo;
59 import android.telephony.ims.ImsStreamMediaProfile;
60 import android.telephony.ims.stub.ImsRegistrationImplBase;
61 import android.util.LongSparseArray;
62 import android.util.SparseArray;
63 import android.util.SparseIntArray;
64 
65 import com.android.internal.annotations.VisibleForTesting;
66 import com.android.internal.telephony.Call;
67 import com.android.internal.telephony.Connection;
68 import com.android.internal.telephony.DriverCall;
69 import com.android.internal.telephony.GsmCdmaConnection;
70 import com.android.internal.telephony.Phone;
71 import com.android.internal.telephony.PhoneConstants;
72 import com.android.internal.telephony.PhoneFactory;
73 import com.android.internal.telephony.ServiceStateTracker;
74 import com.android.internal.telephony.analytics.TelephonyAnalytics;
75 import com.android.internal.telephony.analytics.TelephonyAnalytics.CallAnalytics;
76 import com.android.internal.telephony.flags.FeatureFlags;
77 import com.android.internal.telephony.imsphone.ImsPhone;
78 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
79 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
80 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.AudioCodec;
81 import com.android.internal.telephony.satellite.SatelliteController;
82 import com.android.internal.telephony.uicc.UiccController;
83 import com.android.telephony.Rlog;
84 
85 import java.util.ArrayList;
86 import java.util.Arrays;
87 import java.util.HashSet;
88 import java.util.List;
89 import java.util.Set;
90 import java.util.UUID;
91 import java.util.stream.Collectors;
92 
93 /** Collects voice call events per phone ID for the pulled atom. */
94 public class VoiceCallSessionStats {
95     private static final String TAG = VoiceCallSessionStats.class.getSimpleName();
96 
97     // Upper bounds of each call setup duration category in milliseconds.
98     private static final int CALL_SETUP_DURATION_UNKNOWN = 0;
99     private static final int CALL_SETUP_DURATION_EXTREMELY_FAST = 400;
100     private static final int CALL_SETUP_DURATION_ULTRA_FAST = 700;
101     private static final int CALL_SETUP_DURATION_VERY_FAST = 1000;
102     private static final int CALL_SETUP_DURATION_FAST = 1500;
103     private static final int CALL_SETUP_DURATION_NORMAL = 2500;
104     private static final int CALL_SETUP_DURATION_SLOW = 4000;
105     private static final int CALL_SETUP_DURATION_VERY_SLOW = 6000;
106     private static final int CALL_SETUP_DURATION_ULTRA_SLOW = 10000;
107     // CALL_SETUP_DURATION_EXTREMELY_SLOW has no upper bound (it includes everything above 10000)
108 
109     // Upper bounds of each call duration category in milliseconds.
110     private static final int CALL_DURATION_ONE_MINUTE = 60000;
111     private static final int CALL_DURATION_FIVE_MINUTES = 300000;
112     private static final int CALL_DURATION_TEN_MINUTES = 600000;
113     private static final int CALL_DURATION_THIRTY_MINUTES = 1800000;
114     private static final int CALL_DURATION_ONE_HOUR = 3600000;
115 
116     /** Number of buckets for codec quality, from UNKNOWN to FULLBAND. */
117     private static final int CODEC_QUALITY_COUNT = 5;
118 
119     /**
120      * Threshold to calculate the main audio codec quality of the call.
121      *
122      * <p>The audio codec quality was equal to or greater than the main audio codec quality for at
123      * least 70% of the call.
124      */
125     private static final int MAIN_CODEC_QUALITY_THRESHOLD = 70;
126 
127     /** Holds the audio codec value for CS calls. */
128     private static final SparseIntArray CS_CODEC_MAP = buildGsmCdmaCodecMap();
129 
130     /** Holds the audio codec value for IMS calls. */
131     private static final SparseIntArray IMS_CODEC_MAP = buildImsCodecMap();
132 
133     /** Holds call duration buckets with values as their upper bounds in milliseconds. */
134     private static final SparseIntArray CALL_DURATION_MAP = buildCallDurationMap();
135 
136     /** UUID for reporting concurrent call anomaly */
137     private static final UUID CONCURRENT_CALL_ANOMALY_UUID =
138             UUID.fromString("76780b5a-623e-48a4-be3f-925e05177c9c");
139 
140     /** If the number of concurrent calls exceeds this number, report anomaly*/
141     private static final int MAX_NORMAL_CONCURRENT_CALLS = 3;
142 
143     /**
144      * Tracks statistics for each call connection, indexed with ID returned by {@link
145      * #getConnectionId}.
146      */
147     private final SparseArray<VoiceCallSession> mCallProtos = new SparseArray<>();
148 
149     /**
150      * Tracks usage of codecs for each call.
151      *
152      * <p>The outer array is used to map each connection id to the corresponding codec usage. The
153      * inner array is used to map timestamp (key) with the codec in use (value).
154      */
155     private final SparseArray<LongSparseArray<Integer>> mCodecUsage = new SparseArray<>();
156 
157     /**
158      * Tracks call RAT usage.
159      *
160      * <p>RAT usage is mainly tied to phones rather than calls, since each phone can have multiple
161      * concurrent calls, and we do not want to count the RAT duration multiple times.
162      */
163     private final VoiceCallRatTracker mRatUsage = new VoiceCallRatTracker();
164 
165     private final @NonNull FeatureFlags mFlags;
166     private final int mPhoneId;
167     private final Phone mPhone;
168 
169     private final PersistAtomsStorage mAtomsStorage =
170             PhoneFactory.getMetricsCollector().getAtomsStorage();
171     private final UiccController mUiccController = UiccController.getInstance();
172     private final DeviceStateHelper mDeviceStateHelper =
173             PhoneFactory.getMetricsCollector().getDeviceStateHelper();
174     private final VonrHelper mVonrHelper =
175             PhoneFactory.getMetricsCollector().getVonrHelper();
176     private final SatelliteController mSatelliteController;
177 
VoiceCallSessionStats(int phoneId, Phone phone, @NonNull FeatureFlags featureFlags)178     public VoiceCallSessionStats(int phoneId, Phone phone, @NonNull FeatureFlags featureFlags) {
179         mPhoneId = phoneId;
180         mPhone = phone;
181         mFlags = featureFlags;
182         DataConnectionStateTracker.getInstance(phoneId).start(phone);
183         mSatelliteController = SatelliteController.getInstance();
184     }
185 
186     /* CS calls */
187 
188     /** Updates internal states when previous CS calls are accepted to track MT call setup time. */
onRilAcceptCall(List<Connection> connections)189     public synchronized void onRilAcceptCall(List<Connection> connections) {
190         for (Connection conn : connections) {
191             acceptCall(conn);
192         }
193     }
194 
195     /** Updates internal states when a CS MO call is created. */
onRilDial(Connection conn)196     public synchronized void onRilDial(Connection conn) {
197         addCall(conn);
198     }
199 
200     /**
201      * Updates internal states when CS calls are created or terminated, or CS call state is changed.
202      */
onRilCallListChanged(List<GsmCdmaConnection> connections)203     public synchronized void onRilCallListChanged(List<GsmCdmaConnection> connections) {
204         for (Connection conn : connections) {
205             int id = getConnectionId(conn);
206             if (!mCallProtos.contains(id)) {
207                 // handle new connections
208                 if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
209                     addCall(conn);
210                     checkCallSetup(conn, mCallProtos.get(id));
211                 } else {
212                     logd(
213                             "onRilCallListChanged: skip adding disconnected connection,"
214                                     + " connectionId=%d",
215                             id);
216                 }
217             } else {
218                 VoiceCallSession proto = mCallProtos.get(id);
219                 // handle call state change
220                 checkCallSetup(conn, proto);
221                 // handle terminated connections
222                 if (conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) {
223                     proto.bearerAtEnd = getBearer(conn); // should be CS
224                     proto.disconnectReasonCode = conn.getDisconnectCause();
225                     proto.disconnectExtraCode = conn.getPreciseDisconnectCause();
226                     proto.disconnectExtraMessage = conn.getVendorDisconnectCause();
227                     proto.callDuration = classifyCallDuration(conn.getDurationMillis());
228                     if (mPhone != null) {
229                         TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
230                         if (telephonyAnalytics != null) {
231                             CallAnalytics callAnalytics = telephonyAnalytics.getCallAnalytics();
232                             if (callAnalytics != null) {
233                                 callAnalytics.onCallTerminated(proto.isEmergency,
234                                         false /* isOverIms */,
235                                         getVoiceRatWithVoNRFix(mPhone, mPhone.getServiceState(),
236                                                 proto.bearerAtEnd),
237                                         proto.simSlotIndex, proto.disconnectReasonCode);
238                             }
239                         }
240                     }
241                     finishCall(id);
242                 }
243             }
244         }
245         // NOTE: we cannot check stray connections (CS call in our list but not in RIL), as
246         // GsmCdmaCallTracker can call this with a partial list
247     }
248 
249     /* IMS calls */
250 
251     /** Updates internal states when an IMS MO call is created. */
onImsDial(ImsPhoneConnection conn)252     public synchronized void onImsDial(ImsPhoneConnection conn) {
253         addCall(conn);
254         if (conn.hasRttTextStream()) {
255             setRttStarted(conn);
256         }
257     }
258 
259     /** Updates internal states when an IMS MT call is created. */
onImsCallReceived(ImsPhoneConnection conn)260     public synchronized void onImsCallReceived(ImsPhoneConnection conn) {
261         addCall(conn);
262         if (conn.hasRttTextStream()) {
263             setRttStarted(conn);
264         }
265     }
266 
267     /** Updates internal states when previous IMS calls are accepted to track MT call setup time. */
onImsAcceptCall(List<Connection> connections)268     public synchronized void onImsAcceptCall(List<Connection> connections) {
269         for (Connection conn : connections) {
270             acceptCall(conn);
271         }
272     }
273 
274     /** Updates internal states when an IMS fails to start. */
onImsCallStartFailed( @ullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo)275     public synchronized void onImsCallStartFailed(
276             @Nullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo) {
277         onImsCallTerminated(conn, reasonInfo);
278     }
279 
280     /** Updates internal states when an IMS call is terminated. */
onImsCallTerminated( @ullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo)281     public synchronized void onImsCallTerminated(
282             @Nullable ImsPhoneConnection conn, ImsReasonInfo reasonInfo) {
283         if (conn == null) {
284             List<Integer> imsConnIds = getImsConnectionIds();
285             if (imsConnIds.size() == 1) {
286                 loge("onImsCallTerminated: ending IMS call w/ conn=null");
287                 finishImsCall(imsConnIds.get(0), reasonInfo, 0);
288             } else {
289                 loge("onImsCallTerminated: %d IMS calls w/ conn=null", imsConnIds.size());
290             }
291         } else {
292             int id = getConnectionId(conn);
293             if (mCallProtos.contains(id)) {
294                 finishImsCall(id, reasonInfo, conn.getDurationMillis());
295             } else {
296                 loge("onImsCallTerminated: untracked connection, connectionId=%d", id);
297                 // fake a call so at least some info can be tracked
298                 addCall(conn);
299                 finishImsCall(id, reasonInfo, conn.getDurationMillis());
300             }
301         }
302     }
303 
304     /** Updates internal states when RTT is started on an IMS call. */
onRttStarted(ImsPhoneConnection conn)305     public synchronized void onRttStarted(ImsPhoneConnection conn) {
306         setRttStarted(conn);
307     }
308 
309     /* general & misc. */
310 
311     /** Updates internal states when audio codec for a call is changed. */
onAudioCodecChanged(Connection conn, int audioQuality)312     public synchronized void onAudioCodecChanged(Connection conn, int audioQuality) {
313         int id = getConnectionId(conn);
314         VoiceCallSession proto = mCallProtos.get(id);
315         if (proto == null) {
316             loge("onAudioCodecChanged: untracked connection, connectionId=%d", id);
317             return;
318         }
319         int codec = audioQualityToCodec(proto.bearerAtEnd, audioQuality);
320         proto.codecBitmask |= (1L << codec);
321 
322         if (mCodecUsage.contains(id)) {
323             mCodecUsage.get(id).append(getTimeMillis(), codec);
324         } else {
325             LongSparseArray<Integer> arr = new LongSparseArray<>();
326             arr.append(getTimeMillis(), codec);
327             mCodecUsage.put(id, arr);
328         }
329     }
330 
331     /** Updates internal states when video state changes. */
onVideoStateChange( ImsPhoneConnection conn, @VideoState int videoState)332     public synchronized void onVideoStateChange(
333             ImsPhoneConnection conn, @VideoState int videoState) {
334         int id = getConnectionId(conn);
335         VoiceCallSession proto = mCallProtos.get(id);
336         if (proto == null) {
337             loge("onVideoStateChange: untracked connection, connectionId=%d", id);
338             return;
339         }
340         logd("onVideoStateChange: video state=%d, connectionId=%d", videoState, id);
341         if (videoState != VideoProfile.STATE_AUDIO_ONLY) {
342             proto.videoEnabled = true;
343         }
344     }
345 
346     /** Updates internal states when multiparty state changes. */
onMultipartyChange(ImsPhoneConnection conn, boolean isMultiParty)347     public synchronized void onMultipartyChange(ImsPhoneConnection conn, boolean isMultiParty) {
348         int id = getConnectionId(conn);
349         VoiceCallSession proto = mCallProtos.get(id);
350         if (proto == null) {
351             loge("onMultipartyChange: untracked connection, connectionId=%d", id);
352             return;
353         }
354         logd("onMultipartyChange: isMultiparty=%b, connectionId=%d", isMultiParty, id);
355         if (isMultiParty) {
356             proto.isMultiparty = true;
357         }
358     }
359 
360     /**
361      * Updates internal states when a call changes state to track setup time and status.
362      *
363      * <p>This is currently mainly used by IMS since CS call states are updated through {@link
364      * #onRilCallListChanged}.
365      */
onCallStateChanged(Call call)366     public synchronized void onCallStateChanged(Call call) {
367         for (Connection conn : call.getConnections()) {
368             int id = getConnectionId(conn);
369             VoiceCallSession proto = mCallProtos.get(id);
370             if (proto != null) {
371                 checkCallSetup(conn, proto);
372             } else {
373                 loge("onCallStateChanged: untracked connection, connectionId=%d", id);
374             }
375         }
376     }
377 
378     /** Updates internal states when an IMS call is handover to a CS call. */
onRilSrvccStateChanged(int state)379     public synchronized void onRilSrvccStateChanged(int state) {
380         List<Connection> handoverConnections = null;
381         if (mPhone.getImsPhone() != null) {
382             handoverConnections = mPhone.getImsPhone().getHandoverConnection();
383         } else {
384             loge("onRilSrvccStateChanged: ImsPhone is null");
385         }
386         List<Integer> imsConnIds;
387         if (handoverConnections == null) {
388             imsConnIds = getImsConnectionIds();
389             loge("onRilSrvccStateChanged: ImsPhone has no handover, we have %d", imsConnIds.size());
390         } else {
391             imsConnIds =
392                     handoverConnections.stream()
393                             .map(VoiceCallSessionStats::getConnectionId)
394                             .collect(Collectors.toList());
395         }
396         switch (state) {
397             case TelephonyManager.SRVCC_STATE_HANDOVER_COMPLETED:
398                 // connection will now be CS
399                 for (int id : imsConnIds) {
400                     VoiceCallSession proto = mCallProtos.get(id);
401                     proto.srvccCompleted = true;
402                     proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
403                     // Call RAT may have changed (e.g. IWLAN -> UMTS) due to bearer change
404                     updateRatAtEnd(proto, getVoiceRatWithVoNRFix(
405                             mPhone, mPhone.getServiceState(), proto.bearerAtEnd));
406                 }
407                 break;
408             case TelephonyManager.SRVCC_STATE_HANDOVER_FAILED:
409                 for (int id : imsConnIds) {
410                     mCallProtos.get(id).srvccFailureCount++;
411                 }
412                 break;
413             case TelephonyManager.SRVCC_STATE_HANDOVER_CANCELED:
414                 for (int id : imsConnIds) {
415                     mCallProtos.get(id).srvccCancellationCount++;
416                 }
417                 break;
418             default: // including STARTED and NONE, do nothing
419         }
420     }
421 
422     /** Updates internal states when RAT changes. */
onServiceStateChanged(ServiceState state)423     public synchronized void onServiceStateChanged(ServiceState state) {
424         if (hasCalls()) {
425             updateRatTracker(state);
426         }
427     }
428 
429     /** Updates internal states when IMS/Emergency PDN/PDU state changes */
onPreciseDataConnectionStateChanged( PreciseDataConnectionState connectionState)430     public synchronized void onPreciseDataConnectionStateChanged(
431             PreciseDataConnectionState connectionState) {
432         if (hasCalls()) {
433             updateVoiceCallSessionBearerState(connectionState);
434         }
435     }
436 
437     /* internal */
438 
439     /** Handles ringing MT call getting accepted. */
acceptCall(Connection conn)440     private void acceptCall(Connection conn) {
441         int id = getConnectionId(conn);
442         if (mCallProtos.contains(id)) {
443             logd("acceptCall: resetting setup info, connectionId=%d", id);
444             VoiceCallSession proto = mCallProtos.get(id);
445             proto.setupBeginMillis = getTimeMillis();
446         } else {
447             loge("acceptCall: untracked connection, connectionId=%d", id);
448         }
449     }
450 
451     /**
452      * Adds a call connection.
453      *
454      * <p>Should be called when the call is created, and when setup begins (upon {@code
455      * RilRequest.RIL_REQUEST_ANSWER} or {@code ImsCommand.IMS_CMD_ACCEPT}).
456      */
addCall(Connection conn)457     private void addCall(Connection conn) {
458         int id = getConnectionId(conn);
459         if (mCallProtos.contains(id)) {
460             loge(
461                     "addCall: already tracked connection, connectionId=%d, connectionInfo=%s",
462                     id, conn);
463             return;
464         }
465         int bearer = getBearer(conn);
466         ServiceState serviceState = getServiceState();
467         @NetworkType int rat = getVoiceRatWithVoNRFix(mPhone, serviceState, bearer);
468         @VideoState int videoState = conn.getVideoState();
469         VoiceCallSession proto = new VoiceCallSession();
470 
471         if (mFlags.vonrEnabledMetric()) {
472             mVonrHelper.updateVonrEnabledState();
473         }
474 
475         proto.bearerAtStart = bearer;
476         proto.bearerAtEnd = bearer;
477         proto.direction = getDirection(conn);
478         proto.setupFailed = true;
479         proto.disconnectReasonCode = conn.getDisconnectCause();
480         proto.disconnectExtraCode = conn.getPreciseDisconnectCause();
481         proto.disconnectExtraMessage = conn.getVendorDisconnectCause();
482         proto.ratAtStart = rat;
483         proto.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
484         proto.ratAtEnd = rat;
485         proto.ratSwitchCount = 0L;
486         proto.ratSwitchCountAfterConnected = 0L;
487         proto.codecBitmask = 0L;
488         proto.simSlotIndex = mPhoneId;
489         proto.isMultiSim = SimSlotState.isMultiSim();
490         proto.isEsim = SimSlotState.isEsim(mPhoneId);
491         proto.carrierId = mPhone.getCarrierId();
492         proto.srvccCompleted = false;
493         proto.srvccFailureCount = 0L;
494         proto.srvccCancellationCount = 0L;
495         proto.rttEnabled = false;
496         proto.isEmergency = conn.isEmergencyCall() || conn.isNetworkIdentifiedEmergencyCall();
497         proto.isRoaming = ServiceStateStats.isNetworkRoaming(serviceState);
498         proto.isMultiparty = conn.isMultiparty();
499         proto.lastKnownRat = rat;
500         proto.videoEnabled = videoState != VideoProfile.STATE_AUDIO_ONLY ? true : false;
501         proto.handoverInProgress = isHandoverInProgress(bearer, proto.isEmergency);
502 
503         boolean isCrossSimCall = isCrossSimCall(conn);
504         proto.isIwlanCrossSimAtStart = isCrossSimCall;
505         proto.isIwlanCrossSimAtEnd = isCrossSimCall;
506         proto.isIwlanCrossSimAtConnected = isCrossSimCall;
507 
508         // internal fields for tracking
509         if (getDirection(conn) == VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT) {
510             // MT call setup hasn't begun hence set to 0
511             proto.setupBeginMillis = 0L;
512         } else {
513             proto.setupBeginMillis = getTimeMillis();
514         }
515 
516         // audio codec might have already been set
517         int codec = audioQualityToCodec(bearer, conn.getAudioCodec());
518         if (codec != AudioCodec.AUDIO_CODEC_UNKNOWN) {
519             proto.codecBitmask = (1L << codec);
520         }
521 
522         proto.concurrentCallCountAtStart = mCallProtos.size();
523         if (proto.concurrentCallCountAtStart > MAX_NORMAL_CONCURRENT_CALLS) {
524             AnomalyReporter.reportAnomaly(
525                     CONCURRENT_CALL_ANOMALY_UUID, "Anomalous number of concurrent calls");
526         }
527         mCallProtos.put(id, proto);
528 
529         // RAT call count needs to be updated
530         updateRatTracker(serviceState);
531     }
532 
533     /** Sends the call metrics to persist storage when it is finished. */
finishCall(int connectionId)534     private void finishCall(int connectionId) {
535         VoiceCallSession proto = mCallProtos.get(connectionId);
536         if (proto == null) {
537             loge("finishCall: could not find call to be removed, connectionId=%d", connectionId);
538             return;
539         }
540 
541         // Compute time it took to fail setup (except for MT calls that have never been picked up)
542         if (proto.setupFailed && proto.setupBeginMillis != 0L && proto.setupDurationMillis == 0) {
543             proto.setupDurationMillis = (int) (getTimeMillis() - proto.setupBeginMillis);
544         }
545 
546         mCallProtos.delete(connectionId);
547         proto.concurrentCallCountAtEnd = mCallProtos.size();
548 
549         // Calculate signal strength at the end of the call
550         proto.signalStrengthAtEnd = getSignalStrength(proto.ratAtEnd);
551 
552         // Calculate main codec quality
553         proto.mainCodecQuality = finalizeMainCodecQuality(connectionId);
554 
555         // ensure internal fields are cleared
556         proto.setupBeginMillis = 0L;
557 
558         // sanitize for javanano & StatsEvent
559         if (proto.disconnectExtraMessage == null) {
560             proto.disconnectExtraMessage = "";
561         }
562 
563         // Retry populating carrier ID if it was invalid
564         if (proto.carrierId <= 0) {
565             proto.carrierId = mPhone.getCarrierId();
566         }
567 
568         // Update end RAT
569         updateRatAtEnd(proto, getVoiceRatWithVoNRFix(mPhone, getServiceState(), proto.bearerAtEnd));
570 
571         // Set device fold state
572         proto.foldState = mDeviceStateHelper.getFoldState();
573 
574         if (mFlags.vonrEnabledMetric()) {
575             proto.vonrEnabled = mVonrHelper.getVonrEnabled(mPhone.getSubId());
576         }
577 
578         proto.supportsBusinessCallComposer = isBusinessCallSupported();
579         // 0 is defined as UNKNOWN in Enum
580         proto.callComposerStatus = getCallComposerStatusForPhone() + 1;
581 
582         proto.isNtn = mSatelliteController != null
583                 ? mSatelliteController.isInSatelliteModeForCarrierRoaming(mPhone) : false;
584 
585         mAtomsStorage.addVoiceCallSession(proto);
586 
587         // merge RAT usages to PersistPullers when the call session ends (i.e. no more active calls)
588         if (!hasCalls()) {
589             mRatUsage.conclude(getTimeMillis());
590             mAtomsStorage.addVoiceCallRatUsage(mRatUsage);
591             mRatUsage.clear();
592         }
593     }
594 
setRttStarted(ImsPhoneConnection conn)595     private void setRttStarted(ImsPhoneConnection conn) {
596         int id = getConnectionId(conn);
597         VoiceCallSession proto = mCallProtos.get(id);
598         if (proto == null) {
599             loge("onRttStarted: untracked connection, connectionId=%d", id);
600             return;
601         }
602         // should be IMS w/o SRVCC
603         if (proto.bearerAtStart != getBearer(conn) || proto.bearerAtEnd != getBearer(conn)) {
604             loge("onRttStarted: connection bearer mismatch but proceeding, connectionId=%d", id);
605         }
606         proto.rttEnabled = true;
607     }
608 
609     /** Returns a {@link Set} of Connection IDs so RAT usage can be correctly tracked. */
getConnectionIds()610     private Set<Integer> getConnectionIds() {
611         Set<Integer> ids = new HashSet<>();
612         for (int i = 0; i < mCallProtos.size(); i++) {
613             ids.add(mCallProtos.keyAt(i));
614         }
615         return ids;
616     }
617 
getImsConnectionIds()618     private List<Integer> getImsConnectionIds() {
619         List<Integer> imsConnIds = new ArrayList<>(mCallProtos.size());
620         for (int i = 0; i < mCallProtos.size(); i++) {
621             if (mCallProtos.valueAt(i).bearerAtEnd
622                     == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
623                 imsConnIds.add(mCallProtos.keyAt(i));
624             }
625         }
626         return imsConnIds;
627     }
628 
hasCalls()629     private boolean hasCalls() {
630         return mCallProtos.size() > 0;
631     }
632 
checkCallSetup(Connection conn, VoiceCallSession proto)633     private void checkCallSetup(Connection conn, VoiceCallSession proto) {
634         if (proto.setupBeginMillis != 0L && isSetupFinished(conn.getCall())) {
635             proto.setupDurationMillis = (int) (getTimeMillis() - proto.setupBeginMillis);
636             proto.setupBeginMillis = 0L;
637         }
638         // Clear setupFailed if call now active, but otherwise leave it unchanged
639         // This block is executed only once, when call becomes active for the first time.
640         if (proto.setupFailed && conn.getState() == Call.State.ACTIVE) {
641             proto.setupFailed = false;
642             // Track RAT when voice call is connected.
643             ServiceState serviceState = getServiceState();
644             @NetworkType int rat = getVoiceRatWithVoNRFix(mPhone, serviceState, proto.bearerAtEnd);
645             proto.ratAtConnected = rat;
646             proto.isIwlanCrossSimAtConnected = isCrossSimCall(conn);
647             // Reset list of codecs with the last codec at the present time. In this way, we
648             // track codec quality only after call is connected and not while ringing.
649             resetCodecList(conn);
650         }
651     }
652 
updateRatTracker(ServiceState state)653     private void updateRatTracker(ServiceState state) {
654         // RAT usage is not broken down by bearer. In case a CS call is made while there is IMS
655         // voice registration, this may be inaccurate (i.e. there could be multiple RAT in use, but
656         // we only pick the most feasible one).
657         @NetworkType int rat = getVoiceRatWithVoNRFix(mPhone, state,
658                 VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN);
659         mRatUsage.add(mPhone.getCarrierId(), rat, getTimeMillis(), getConnectionIds());
660 
661         for (int i = 0; i < mCallProtos.size(); i++) {
662             VoiceCallSession proto = mCallProtos.valueAt(i);
663             rat = getVoiceRatWithVoNRFix(mPhone, state, proto.bearerAtEnd);
664             updateRatAtEnd(proto, rat);
665             proto.bandAtEnd = (rat == TelephonyManager.NETWORK_TYPE_IWLAN)
666                             ? 0
667                             : ServiceStateStats.getBand(state);
668             // assuming that SIM carrier ID does not change during the call
669         }
670     }
671 
updateRatAtEnd(VoiceCallSession proto, @NetworkType int rat)672     private void updateRatAtEnd(VoiceCallSession proto, @NetworkType int rat) {
673         if (proto.ratAtEnd != rat) {
674             proto.ratSwitchCount++;
675             if (!proto.setupFailed) {
676                 proto.ratSwitchCountAfterConnected++;
677             }
678             proto.ratAtEnd = rat;
679             if (rat != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
680                 proto.lastKnownRat = rat;
681             }
682         }
683         proto.isIwlanCrossSimAtEnd = isCrossSimCall(mPhone);
684     }
685 
finishImsCall(int id, ImsReasonInfo reasonInfo, long durationMillis)686     private void finishImsCall(int id, ImsReasonInfo reasonInfo, long durationMillis) {
687         VoiceCallSession proto = mCallProtos.get(id);
688         proto.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
689         proto.disconnectReasonCode = reasonInfo.mCode;
690         proto.disconnectExtraCode = reasonInfo.mExtraCode;
691         proto.disconnectExtraMessage = ImsStats.filterExtraMessage(reasonInfo.mExtraMessage);
692         proto.callDuration = classifyCallDuration(durationMillis);
693         if (mPhone != null) {
694             TelephonyAnalytics telephonyAnalytics = mPhone.getTelephonyAnalytics();
695             if (telephonyAnalytics != null) {
696                 CallAnalytics callAnalytics = telephonyAnalytics.getCallAnalytics();
697                 if (callAnalytics != null) {
698                     callAnalytics.onCallTerminated(proto.isEmergency, true /* isOverIms */ ,
699                             getVoiceRatWithVoNRFix(
700                                     mPhone, mPhone.getServiceState(), proto.bearerAtEnd),
701                             proto.simSlotIndex, proto.disconnectReasonCode);
702                 }
703             }
704         }
705         finishCall(id);
706     }
707 
getServiceState()708     private @Nullable ServiceState getServiceState() {
709         ServiceStateTracker tracker = mPhone.getServiceStateTracker();
710         return tracker != null ? tracker.getServiceState() : null;
711     }
712 
getDirection(Connection conn)713     private static int getDirection(Connection conn) {
714         return conn.isIncoming()
715                 ? VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT
716                 : VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO;
717     }
718 
getBearer(Connection conn)719     private static int getBearer(Connection conn) {
720         int phoneType = conn.getPhoneType();
721         switch (phoneType) {
722             case PhoneConstants.PHONE_TYPE_GSM:
723             case PhoneConstants.PHONE_TYPE_CDMA:
724                 return VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
725             case PhoneConstants.PHONE_TYPE_IMS:
726                 return VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
727             default:
728                 loge("getBearer: unknown phoneType=%d", phoneType);
729                 return VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN;
730         }
731     }
732 
733     /** Returns the signal strength. */
getSignalStrength(@etworkType int rat)734     private int getSignalStrength(@NetworkType int rat) {
735         if (rat == TelephonyManager.NETWORK_TYPE_IWLAN) {
736             return getSignalStrengthWifi();
737         } else {
738             return getSignalStrengthCellular();
739         }
740     }
741 
742     /** Returns the signal strength of WiFi. */
getSignalStrengthWifi()743     private int getSignalStrengthWifi() {
744         WifiManager wifiManager =
745                 (WifiManager) mPhone.getContext().getSystemService(Context.WIFI_SERVICE);
746         WifiInfo wifiInfo = wifiManager.getConnectionInfo();
747         int result = VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
748         if (wifiInfo != null) {
749             int level = wifiManager.calculateSignalLevel(wifiInfo.getRssi());
750             int max = wifiManager.getMaxSignalLevel();
751             // Scale result into 0 to 4 range.
752             result =
753                     VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT * level / max;
754             logd("WiFi level: " + result + " (" + level + "/" + max + ")");
755         }
756         return result;
757     }
758 
759     /** Returns the signal strength of cellular RAT. */
getSignalStrengthCellular()760     private int getSignalStrengthCellular() {
761         return mPhone.getSignalStrength().getLevel();
762     }
763 
isHandoverInProgress(int bearer, boolean isEmergency)764     private boolean isHandoverInProgress(int bearer, boolean isEmergency) {
765         // If the call is not IMS, the bearer will not be able to handover
766         if (bearer != VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
767             return false;
768         }
769 
770         int apnType = isEmergency ? ApnSetting.TYPE_EMERGENCY : ApnSetting.TYPE_IMS;
771         int dataState = DataConnectionStateTracker.getInstance(mPhoneId).getDataState(apnType);
772         return dataState == TelephonyManager.DATA_HANDOVER_IN_PROGRESS;
773     }
774 
775     /**
776      * This is a copy of ServiceStateStats.getVoiceRat(Phone, ServiceState, int) with minimum fix
777      * required for tracking EPSFB correctly.
778      */
getVoiceRatWithVoNRFix( Phone phone, @Nullable ServiceState state, int bearer)779     @VisibleForTesting private static @NetworkType int getVoiceRatWithVoNRFix(
780             Phone phone, @Nullable ServiceState state, int bearer) {
781         if (state == null) {
782             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
783         }
784         ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
785         if (bearer != VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS && imsPhone != null) {
786             @NetworkType int imsVoiceRat = imsPhone.getImsStats().getImsVoiceRadioTech();
787             @NetworkType int wwanPsRat =
788                     ServiceStateStats.getRat(state, NetworkRegistrationInfo.DOMAIN_PS);
789             if (imsVoiceRat != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
790                 // If IMS is registered over WWAN but WWAN PS is not in service,
791                 // fallback to WWAN CS RAT
792                 boolean isImsVoiceRatValid =
793                         (imsVoiceRat == TelephonyManager.NETWORK_TYPE_IWLAN
794                                 || wwanPsRat != TelephonyManager.NETWORK_TYPE_UNKNOWN);
795                 if (isImsVoiceRatValid) {
796                     // Fix for VoNR and EPSFB, b/277906557
797                     @NetworkType int oldRat = ServiceStateStats.getVoiceRat(phone, state, bearer),
798                             rat = imsVoiceRat == TelephonyManager.NETWORK_TYPE_IWLAN
799                                     ? imsVoiceRat : wwanPsRat;
800                     logd("getVoiceRatWithVoNRFix: oldRat=%d, newRat=%d", oldRat, rat);
801                     return rat;
802                 }
803             }
804         }
805 
806         return ServiceStateStats.getRat(state, NetworkRegistrationInfo.DOMAIN_CS);
807     }
808 
809     /** Resets the list of codecs used for the connection with only the codec currently in use. */
resetCodecList(Connection conn)810     private void resetCodecList(Connection conn) {
811         int id = getConnectionId(conn);
812         LongSparseArray<Integer> codecUsage = mCodecUsage.get(id);
813         if (codecUsage != null) {
814             int lastCodec = codecUsage.valueAt(codecUsage.size() - 1);
815             LongSparseArray<Integer> arr = new LongSparseArray<>();
816             arr.append(getTimeMillis(), lastCodec);
817             mCodecUsage.put(id, arr);
818         }
819     }
820 
821     /** Returns the main codec quality used during the call. */
finalizeMainCodecQuality(int connectionId)822     private int finalizeMainCodecQuality(int connectionId) {
823         // Retrieve information about codec usage for this call and remove it from main array.
824         if (!mCodecUsage.contains(connectionId)) {
825             return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
826         }
827         LongSparseArray<Integer> codecUsage = mCodecUsage.get(connectionId);
828         mCodecUsage.delete(connectionId);
829 
830         // Append fake entry at the end, to facilitate the calculation of time for each codec.
831         codecUsage.put(getTimeMillis(), AudioCodec.AUDIO_CODEC_UNKNOWN);
832 
833         // Calculate array with time for each quality
834         int totalTime = 0;
835         long[] timePerQuality = new long[CODEC_QUALITY_COUNT];
836         for (int i = 0; i < codecUsage.size() - 1; i++) {
837             long time = codecUsage.keyAt(i + 1) - codecUsage.keyAt(i);
838             int quality = getCodecQuality(codecUsage.valueAt(i));
839             timePerQuality[quality] += time;
840             totalTime += time;
841         }
842         logd("Time per codec quality = " + Arrays.toString(timePerQuality));
843 
844         // We calculate 70% duration of the call as the threshold for the main audio codec quality
845         // and iterate on all codec qualities. As soon as the sum of codec duration is greater than
846         // the threshold, we have identified the main codec quality.
847         long timeAtMinimumQuality = 0;
848         long timeThreshold = totalTime * MAIN_CODEC_QUALITY_THRESHOLD / 100;
849         for (int i = CODEC_QUALITY_COUNT - 1; i >= 0; i--) {
850             timeAtMinimumQuality += timePerQuality[i];
851             if (timeAtMinimumQuality >= timeThreshold) {
852                 return i;
853             }
854         }
855         return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
856     }
857 
getCodecQuality(int codec)858     private int getCodecQuality(int codec) {
859         switch (codec) {
860             case AudioCodec.AUDIO_CODEC_AMR:
861             case AudioCodec.AUDIO_CODEC_QCELP13K:
862             case AudioCodec.AUDIO_CODEC_EVRC:
863             case AudioCodec.AUDIO_CODEC_EVRC_B:
864             case AudioCodec.AUDIO_CODEC_EVRC_NW:
865             case AudioCodec.AUDIO_CODEC_GSM_EFR:
866             case AudioCodec.AUDIO_CODEC_GSM_FR:
867             case AudioCodec.AUDIO_CODEC_GSM_HR:
868             case AudioCodec.AUDIO_CODEC_G711U:
869             case AudioCodec.AUDIO_CODEC_G723:
870             case AudioCodec.AUDIO_CODEC_G711A:
871             case AudioCodec.AUDIO_CODEC_G722:
872             case AudioCodec.AUDIO_CODEC_G711AB:
873             case AudioCodec.AUDIO_CODEC_G729:
874             case AudioCodec.AUDIO_CODEC_EVS_NB:
875                 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
876             case AudioCodec.AUDIO_CODEC_AMR_WB:
877             case AudioCodec.AUDIO_CODEC_EVS_WB:
878             case AudioCodec.AUDIO_CODEC_EVRC_WB:
879                 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_WIDEBAND;
880             case AudioCodec.AUDIO_CODEC_EVS_SWB:
881                 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
882             case AudioCodec.AUDIO_CODEC_EVS_FB:
883                 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_FULLBAND;
884             default:
885                 return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
886         }
887     }
888 
isSetupFinished(@ullable Call call)889     private static boolean isSetupFinished(@Nullable Call call) {
890         // NOTE: when setup is finished for MO calls, it is not successful yet.
891         if (call != null) {
892             switch (call.getState()) {
893                 case ACTIVE: // MT setup: accepted to ACTIVE
894                 case ALERTING: // MO setup: dial to ALERTING
895                     return true;
896                 default: // do nothing
897             }
898         }
899         return false;
900     }
901 
audioQualityToCodec(int bearer, int audioQuality)902     private static int audioQualityToCodec(int bearer, int audioQuality) {
903         switch (bearer) {
904             case VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS:
905                 return CS_CODEC_MAP.get(audioQuality, AudioCodec.AUDIO_CODEC_UNKNOWN);
906             case VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS:
907                 return IMS_CODEC_MAP.get(audioQuality, AudioCodec.AUDIO_CODEC_UNKNOWN);
908             default:
909                 loge("audioQualityToCodec: unknown bearer %d", bearer);
910                 return AudioCodec.AUDIO_CODEC_UNKNOWN;
911         }
912     }
913 
classifyCallDuration(long durationMillis)914     private static int classifyCallDuration(long durationMillis) {
915         if (durationMillis == 0L) {
916             return VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_UNKNOWN;
917         }
918         // keys in CALL_SETUP_DURATION_MAP are upper bounds in ascending order
919         for (int i = 0; i < CALL_DURATION_MAP.size(); i++) {
920             if (durationMillis < CALL_DURATION_MAP.keyAt(i)) {
921                 return CALL_DURATION_MAP.valueAt(i);
922             }
923         }
924         return VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_MORE_THAN_ONE_HOUR;
925     }
926 
927     /**
928      * Generates an ID for each connection, which should be the same for IMS and CS connections
929      * involved in the same SRVCC.
930      *
931      * <p>Among the fields copied from ImsPhoneConnection to GsmCdmaConnection during SRVCC, the
932      * Connection's create time seems to be the best choice for ID (assuming no multiple calls in a
933      * millisecond). The 64-bit time is truncated to 32-bit so it can be used as an index in various
934      * data structures, which is good for calls shorter than 49 days.
935      */
getConnectionId(Connection conn)936     private static int getConnectionId(Connection conn) {
937         return conn == null ? 0 : (int) conn.getCreateTime();
938     }
939 
isCrossSimCall(Connection conn)940     private boolean isCrossSimCall(Connection conn) {
941         if (conn instanceof ImsPhoneConnection) {
942             return ((ImsPhoneConnection) conn).isCrossSimCall();
943         }
944         return false;
945     }
946 
isCrossSimCall(Phone phone)947     private boolean isCrossSimCall(Phone phone) {
948         if (phone.getImsPhone() != null) {
949             return phone.getImsPhone().getImsRegistrationTech()
950                     == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
951         }
952         return false;
953     }
954 
getCallComposerStatusForPhone()955     private @CallComposerStatus int getCallComposerStatusForPhone() {
956         TelephonyManager telephonyManager = mPhone.getContext()
957                 .getSystemService(TelephonyManager.class);
958         if (telephonyManager == null) {
959             return TelephonyManager.CALL_COMPOSER_STATUS_OFF;
960         }
961         telephonyManager = telephonyManager.createForSubscriptionId(mPhone.getSubId());
962         return telephonyManager.getCallComposerStatus();
963     }
964 
isBusinessCallSupported()965     private boolean isBusinessCallSupported() {
966         CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
967                 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
968         if (carrierConfigManager == null) {
969             return false;
970         }
971         int subId = mPhone.getSubId();
972         PersistableBundle b = null;
973         try {
974             b = carrierConfigManager.getConfigForSubId(subId,
975                     CarrierConfigManager.KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL);
976         } catch (RuntimeException e) {
977             loge("CarrierConfigLoader is not available.");
978         }
979         if (b == null || b.isEmpty()) {
980             return false;
981         }
982         return b.getBoolean(CarrierConfigManager.KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL);
983     }
984 
985     @VisibleForTesting
getTimeMillis()986     protected long getTimeMillis() {
987         return SystemClock.elapsedRealtime();
988     }
989 
logd(String format, Object... args)990     private static void logd(String format, Object... args) {
991         Rlog.d(TAG, String.format(format, args));
992     }
993 
loge(String format, Object... args)994     private static void loge(String format, Object... args) {
995         Rlog.e(TAG, String.format(format, args));
996     }
997 
buildGsmCdmaCodecMap()998     private static SparseIntArray buildGsmCdmaCodecMap() {
999         SparseIntArray map = new SparseIntArray();
1000         map.put(DriverCall.AUDIO_QUALITY_AMR, AudioCodec.AUDIO_CODEC_AMR);
1001         map.put(DriverCall.AUDIO_QUALITY_AMR_WB, AudioCodec.AUDIO_CODEC_AMR_WB);
1002         map.put(DriverCall.AUDIO_QUALITY_GSM_EFR, AudioCodec.AUDIO_CODEC_GSM_EFR);
1003         map.put(DriverCall.AUDIO_QUALITY_GSM_FR, AudioCodec.AUDIO_CODEC_GSM_FR);
1004         map.put(DriverCall.AUDIO_QUALITY_GSM_HR, AudioCodec.AUDIO_CODEC_GSM_HR);
1005         map.put(DriverCall.AUDIO_QUALITY_EVRC, AudioCodec.AUDIO_CODEC_EVRC);
1006         map.put(DriverCall.AUDIO_QUALITY_EVRC_B, AudioCodec.AUDIO_CODEC_EVRC_B);
1007         map.put(DriverCall.AUDIO_QUALITY_EVRC_WB, AudioCodec.AUDIO_CODEC_EVRC_WB);
1008         map.put(DriverCall.AUDIO_QUALITY_EVRC_NW, AudioCodec.AUDIO_CODEC_EVRC_NW);
1009         return map;
1010     }
1011 
buildImsCodecMap()1012     private static SparseIntArray buildImsCodecMap() {
1013         SparseIntArray map = new SparseIntArray();
1014         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR, AudioCodec.AUDIO_CODEC_AMR);
1015         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB, AudioCodec.AUDIO_CODEC_AMR_WB);
1016         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K, AudioCodec.AUDIO_CODEC_QCELP13K);
1017         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC, AudioCodec.AUDIO_CODEC_EVRC);
1018         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B, AudioCodec.AUDIO_CODEC_EVRC_B);
1019         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB, AudioCodec.AUDIO_CODEC_EVRC_WB);
1020         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW, AudioCodec.AUDIO_CODEC_EVRC_NW);
1021         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR, AudioCodec.AUDIO_CODEC_GSM_EFR);
1022         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR, AudioCodec.AUDIO_CODEC_GSM_FR);
1023         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR, AudioCodec.AUDIO_CODEC_GSM_HR);
1024         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711U, AudioCodec.AUDIO_CODEC_G711U);
1025         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G723, AudioCodec.AUDIO_CODEC_G723);
1026         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711A, AudioCodec.AUDIO_CODEC_G711A);
1027         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G722, AudioCodec.AUDIO_CODEC_G722);
1028         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711AB, AudioCodec.AUDIO_CODEC_G711AB);
1029         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G729, AudioCodec.AUDIO_CODEC_G729);
1030         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB, AudioCodec.AUDIO_CODEC_EVS_NB);
1031         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB, AudioCodec.AUDIO_CODEC_EVS_WB);
1032         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB, AudioCodec.AUDIO_CODEC_EVS_SWB);
1033         map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB, AudioCodec.AUDIO_CODEC_EVS_FB);
1034         return map;
1035     }
1036 
buildCallDurationMap()1037     private static SparseIntArray buildCallDurationMap() {
1038         SparseIntArray map = new SparseIntArray();
1039 
1040         map.put(
1041                 CALL_DURATION_ONE_MINUTE,
1042                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_MINUTE);
1043         map.put(
1044                 CALL_DURATION_FIVE_MINUTES,
1045                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_FIVE_MINUTES);
1046         map.put(
1047                 CALL_DURATION_TEN_MINUTES,
1048                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_TEN_MINUTES);
1049         map.put(
1050                 CALL_DURATION_THIRTY_MINUTES,
1051                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_THIRTY_MINUTES);
1052         map.put(
1053                 CALL_DURATION_ONE_HOUR,
1054                 VOICE_CALL_SESSION__CALL_DURATION__CALL_DURATION_LESS_THAN_ONE_HOUR);
1055         // anything above would be MORE_THAN_ONE_HOUR
1056 
1057         return map;
1058     }
1059 
updateVoiceCallSessionBearerState(PreciseDataConnectionState connectionState)1060     private void updateVoiceCallSessionBearerState(PreciseDataConnectionState connectionState) {
1061         ApnSetting apnSetting = connectionState.getApnSetting();
1062         if (apnSetting == null) {
1063             return;
1064         }
1065 
1066         int apnTypes = apnSetting.getApnTypeBitmask();
1067         if ((apnTypes & ApnSetting.TYPE_IMS) == 0
1068                 && (apnTypes & ApnSetting.TYPE_EMERGENCY) == 0) {
1069             return;
1070         }
1071 
1072         for (int i = 0; i < mCallProtos.size(); i++) {
1073             VoiceCallSession proto = mCallProtos.valueAt(i);
1074             if (proto.bearerAtEnd == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
1075                 if (!proto.isEmergency && (apnTypes & ApnSetting.TYPE_IMS) != 0) {
1076                     updateHandoverState(proto, connectionState.getState());
1077                 }
1078                 if (proto.isEmergency && (apnTypes & ApnSetting.TYPE_EMERGENCY) != 0) {
1079                     updateHandoverState(proto, connectionState.getState());
1080                 }
1081             }
1082         }
1083     }
1084 
updateHandoverState(VoiceCallSession proto, int dataState)1085     private void updateHandoverState(VoiceCallSession proto, int dataState) {
1086         switch (dataState) {
1087             case TelephonyManager.DATA_HANDOVER_IN_PROGRESS:
1088                 proto.handoverInProgress = true;
1089                 break;
1090             default:
1091                 // All other states are considered as not in handover
1092                 proto.handoverInProgress = false;
1093         }
1094     }
1095 }
1096