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