1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.telephony.metrics;
18 
19 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
20 
21 import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_IMS;
22 import static com.android.internal.telephony.InboundSmsHandler.SOURCE_NOT_INJECTED;
23 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
24 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS;
25 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
26 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL;
27 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
28 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
29 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND;
30 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS;
31 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
32 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
33 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
34 import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.NUM_SIGNAL_LEVEL;
35 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IP;
36 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
37 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV6;
38 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_NON_IP;
39 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_PPP;
40 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_UNSTRUCTURED;
41 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_UNKNOWN;
42 
43 import android.content.Context;
44 import android.net.NetworkCapabilities;
45 import android.os.BatteryStatsManager;
46 import android.os.Build;
47 import android.os.SystemClock;
48 import android.os.SystemProperties;
49 import android.provider.Telephony.Sms.Intents;
50 import android.telephony.AccessNetworkConstants;
51 import android.telephony.Annotation.RadioPowerState;
52 import android.telephony.CallQuality;
53 import android.telephony.DisconnectCause;
54 import android.telephony.NetworkRegistrationInfo;
55 import android.telephony.ServiceState;
56 import android.telephony.SmsManager;
57 import android.telephony.SmsMessage;
58 import android.telephony.SubscriptionInfo;
59 import android.telephony.SubscriptionManager;
60 import android.telephony.TelephonyHistogram;
61 import android.telephony.TelephonyManager;
62 import android.telephony.TelephonyManager.PrefNetworkMode;
63 import android.telephony.data.DataCallResponse;
64 import android.telephony.data.DataService;
65 import android.telephony.emergency.EmergencyNumber;
66 import android.telephony.ims.ImsCallProfile;
67 import android.telephony.ims.ImsCallSession;
68 import android.telephony.ims.ImsReasonInfo;
69 import android.telephony.ims.ImsStreamMediaProfile;
70 import android.telephony.ims.feature.MmTelFeature;
71 import android.telephony.ims.stub.ImsRegistrationImplBase;
72 import android.telephony.ims.stub.ImsSmsImplBase;
73 import android.text.TextUtils;
74 import android.util.ArrayMap;
75 import android.util.Base64;
76 import android.util.SparseArray;
77 
78 import com.android.internal.telephony.CarrierResolver;
79 import com.android.internal.telephony.DriverCall;
80 import com.android.internal.telephony.GsmCdmaConnection;
81 import com.android.internal.telephony.InboundSmsHandler;
82 import com.android.internal.telephony.PhoneConstants;
83 import com.android.internal.telephony.RIL;
84 import com.android.internal.telephony.RILConstants;
85 import com.android.internal.telephony.SmsController;
86 import com.android.internal.telephony.SmsResponse;
87 import com.android.internal.telephony.UUSInfo;
88 import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator;
89 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
90 import com.android.internal.telephony.imsphone.ImsPhoneCall;
91 import com.android.internal.telephony.nano.TelephonyProto;
92 import com.android.internal.telephony.nano.TelephonyProto.ActiveSubscriptionInfo;
93 import com.android.internal.telephony.nano.TelephonyProto.BandwidthEstimatorStats;
94 import com.android.internal.telephony.nano.TelephonyProto.EmergencyNumberInfo;
95 import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
96 import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
97 import com.android.internal.telephony.nano.TelephonyProto.ModemPowerStats;
98 import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
99 import com.android.internal.telephony.nano.TelephonyProto.SimState;
100 import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
101 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
102 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.CallState;
103 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall;
104 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall.Type;
105 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
106 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatching;
107 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatchingResult;
108 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierKeyChange;
109 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.DataSwitch;
110 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
111 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.NetworkCapabilitiesInfo;
112 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.OnDemandDataSwitch;
113 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RadioState;
114 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
115 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall.DeactivateReason;
116 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
117 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
118 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse.RilDataCallFailCause;
119 import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog;
120 import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
121 import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
122 import com.android.internal.telephony.nano.TelephonyProto.TimeInterval;
123 import com.android.internal.telephony.protobuf.nano.MessageNano;
124 import com.android.internal.telephony.util.TelephonyUtils;
125 import com.android.internal.util.IndentingPrintWriter;
126 import com.android.telephony.Rlog;
127 
128 import java.io.FileDescriptor;
129 import java.io.PrintWriter;
130 import java.text.DecimalFormat;
131 import java.util.ArrayDeque;
132 import java.util.ArrayList;
133 import java.util.Arrays;
134 import java.util.Deque;
135 import java.util.List;
136 import java.util.Map;
137 import java.util.concurrent.ThreadLocalRandom;
138 
139 /**
140  * Telephony metrics holds all metrics events and convert it into telephony proto buf.
141  * @hide
142  */
143 public class TelephonyMetrics {
144 
145     private static final String TAG = TelephonyMetrics.class.getSimpleName();
146 
147     private static final boolean DBG = true;
148     private static final boolean VDBG = false; // STOPSHIP if true
149 
150     /** Maximum telephony events stored */
151     private static final int MAX_TELEPHONY_EVENTS = 1000;
152 
153     /** Maximum call sessions stored */
154     private static final int MAX_COMPLETED_CALL_SESSIONS = 50;
155 
156     /** Maximum sms sessions stored */
157     private static final int MAX_COMPLETED_SMS_SESSIONS = 500;
158 
159     /** For reducing the timing precision for privacy purposes */
160     private static final int SESSION_START_PRECISION_MINUTES = 5;
161 
162     /** The TelephonyMetrics singleton instance */
163     private static TelephonyMetrics sInstance;
164 
165     /** Telephony events */
166     private final Deque<TelephonyEvent> mTelephonyEvents = new ArrayDeque<>();
167 
168     /**
169      * In progress call sessions. Note that each phone can only have up to 1 in progress call
170      * session (might contains multiple calls). Having a sparse array in case we need to support
171      * DSDA in the future.
172      */
173     private final SparseArray<InProgressCallSession> mInProgressCallSessions = new SparseArray<>();
174 
175     /** The completed call sessions */
176     private final Deque<TelephonyCallSession> mCompletedCallSessions = new ArrayDeque<>();
177 
178     /** The in-progress SMS sessions. When finished, it will be moved into the completed sessions */
179     private final SparseArray<InProgressSmsSession> mInProgressSmsSessions = new SparseArray<>();
180 
181     /** The completed SMS sessions */
182     private final Deque<SmsSession> mCompletedSmsSessions = new ArrayDeque<>();
183 
184     /** Last service state. This is for injecting the base of a new log or a new call/sms session */
185     private final SparseArray<TelephonyServiceState> mLastServiceState = new SparseArray<>();
186 
187     /**
188      * Last ims capabilities. This is for injecting the base of a new log or a new call/sms session
189      */
190     private final SparseArray<ImsCapabilities> mLastImsCapabilities = new SparseArray<>();
191 
192     /**
193      * Last IMS connection state. This is for injecting the base of a new log or a new call/sms
194      * session
195      */
196     private final SparseArray<ImsConnectionState> mLastImsConnectionState = new SparseArray<>();
197 
198     /** Last settings state. This is for deduping same settings event logged. */
199     private final SparseArray<TelephonySettings> mLastSettings = new SparseArray<>();
200 
201     /** Last sim state, indexed by phone id. */
202     private final SparseArray<Integer> mLastSimState = new SparseArray<>();
203 
204     /** Last radio state, indexed by phone id. */
205     private final SparseArray<Integer> mLastRadioState = new SparseArray<>();
206 
207     /** Last active subscription information, indexed by phone id. */
208     private final SparseArray<ActiveSubscriptionInfo> mLastActiveSubscriptionInfos =
209             new SparseArray<>();
210 
211     /**
212      * The last modem state represent by a bitmap, the i-th bit(LSB) indicates the i-th modem
213      * state(0 - disabled, 1 - enabled).
214      *
215      * TODO: initialize the enabled modem bitmap when it's possible to get the modem state.
216      */
217     private int mLastEnabledModemBitmap = (1 << TelephonyManager.getDefault().getPhoneCount()) - 1;
218 
219     /** Last carrier id matching. */
220     private final SparseArray<CarrierIdMatching> mLastCarrierId = new SparseArray<>();
221 
222     /** Last NetworkCapabilitiesInfo, indexed by phone id. */
223     private final SparseArray<NetworkCapabilitiesInfo> mLastNetworkCapabilitiesInfos =
224             new SparseArray<>();
225 
226     /** Last RilDataCall Events (indexed by cid), indexed by phone id */
227     private final SparseArray<SparseArray<RilDataCall>> mLastRilDataCallEvents =
228             new SparseArray<>();
229 
230     /** List of Tx and Rx Bandwidth estimation stats maps */
231     private final List<Map<String, BwEstimationStats>> mBwEstStatsMapList = new ArrayList<>(
232             Arrays.asList(new ArrayMap<>(), new ArrayMap<>()));
233 
234     /** The start system time of the TelephonyLog in milliseconds*/
235     private long mStartSystemTimeMs;
236 
237     /** The start elapsed time of the TelephonyLog in milliseconds*/
238     private long mStartElapsedTimeMs;
239 
240     /** Indicating if some of the telephony events are dropped in this log */
241     private boolean mTelephonyEventsDropped = false;
242 
243     private Context mContext;
244 
TelephonyMetrics()245     public TelephonyMetrics() {
246         mStartSystemTimeMs = System.currentTimeMillis();
247         mStartElapsedTimeMs = SystemClock.elapsedRealtime();
248     }
249 
250     /**
251      * Get the singleton instance of telephony metrics.
252      *
253      * @return The instance
254      */
getInstance()255     public synchronized static TelephonyMetrics getInstance() {
256         if (sInstance == null) {
257             sInstance = new TelephonyMetrics();
258         }
259 
260         return sInstance;
261     }
262 
263     /**
264      * Set the context for telephony metrics.
265      *
266      * @param context Context
267      * @hide
268      */
setContext(Context context)269     public void setContext(Context context) {
270         mContext = context;
271     }
272 
273     /**
274      * Dump the state of various objects, add calls to other objects as desired.
275      *
276      * @param fd File descriptor
277      * @param pw Print writer
278      * @param args Arguments
279      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)280     public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
281         if (args != null && args.length > 0) {
282             boolean reset = true;
283             if (args.length > 1 && "--keep".equals(args[1])) {
284                 reset = false;
285             }
286 
287             switch (args[0]) {
288                 case "--metrics":
289                     printAllMetrics(pw);
290                     break;
291                 case "--metricsproto":
292                     pw.println(convertProtoToBase64String(buildProto()));
293                     pw.println(RcsStats.getInstance().buildLog());
294                     if (reset) {
295                         reset();
296                     }
297                     break;
298                 case "--metricsprototext":
299                     pw.println(buildProto().toString());
300                     pw.println(RcsStats.getInstance().buildProto().toString());
301                     break;
302             }
303         }
304     }
305 
logv(String log)306     private void logv(String log) {
307         if (VDBG) {
308             Rlog.v(TAG, log);
309         }
310     }
311 
312     /**
313      * Convert the telephony event to string
314      *
315      * @param event The event in integer
316      * @return The event in string
317      */
telephonyEventToString(int event)318     private static String telephonyEventToString(int event) {
319         switch (event) {
320             case TelephonyEvent.Type.UNKNOWN:
321                 return "UNKNOWN";
322             case TelephonyEvent.Type.SETTINGS_CHANGED:
323                 return "SETTINGS_CHANGED";
324             case TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED:
325                 return "RIL_SERVICE_STATE_CHANGED";
326             case TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED:
327                 return "IMS_CONNECTION_STATE_CHANGED";
328             case TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED:
329                 return "IMS_CAPABILITIES_CHANGED";
330             case TelephonyEvent.Type.DATA_CALL_SETUP:
331                 return "DATA_CALL_SETUP";
332             case TelephonyEvent.Type.DATA_CALL_SETUP_RESPONSE:
333                 return "DATA_CALL_SETUP_RESPONSE";
334             case TelephonyEvent.Type.DATA_CALL_LIST_CHANGED:
335                 return "DATA_CALL_LIST_CHANGED";
336             case TelephonyEvent.Type.DATA_CALL_DEACTIVATE:
337                 return "DATA_CALL_DEACTIVATE";
338             case TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE:
339                 return "DATA_CALL_DEACTIVATE_RESPONSE";
340             case TelephonyEvent.Type.DATA_STALL_ACTION:
341                 return "DATA_STALL_ACTION";
342             case TelephonyEvent.Type.MODEM_RESTART:
343                 return "MODEM_RESTART";
344             case TelephonyEvent.Type.CARRIER_ID_MATCHING:
345                 return "CARRIER_ID_MATCHING";
346             case TelephonyEvent.Type.NITZ_TIME:
347                 return "NITZ_TIME";
348             case TelephonyEvent.Type.EMERGENCY_NUMBER_REPORT:
349                 return "EMERGENCY_NUMBER_REPORT";
350             case TelephonyEvent.Type.NETWORK_CAPABILITIES_CHANGED:
351                 return "NETWORK_CAPABILITIES_CHANGED";
352             default:
353                 return Integer.toString(event);
354         }
355     }
356 
357     /**
358      * Convert the call session event into string
359      *
360      * @param event The event in integer
361      * @return The event in String
362      */
callSessionEventToString(int event)363     private static String callSessionEventToString(int event) {
364         switch (event) {
365             case TelephonyCallSession.Event.Type.EVENT_UNKNOWN:
366                 return "EVENT_UNKNOWN";
367             case TelephonyCallSession.Event.Type.SETTINGS_CHANGED:
368                 return "SETTINGS_CHANGED";
369             case TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED:
370                 return "RIL_SERVICE_STATE_CHANGED";
371             case TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED:
372                 return "IMS_CONNECTION_STATE_CHANGED";
373             case TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED:
374                 return "IMS_CAPABILITIES_CHANGED";
375             case TelephonyCallSession.Event.Type.DATA_CALL_LIST_CHANGED:
376                 return "DATA_CALL_LIST_CHANGED";
377             case TelephonyCallSession.Event.Type.RIL_REQUEST:
378                 return "RIL_REQUEST";
379             case TelephonyCallSession.Event.Type.RIL_RESPONSE:
380                 return "RIL_RESPONSE";
381             case TelephonyCallSession.Event.Type.RIL_CALL_RING:
382                 return "RIL_CALL_RING";
383             case TelephonyCallSession.Event.Type.RIL_CALL_SRVCC:
384                 return "RIL_CALL_SRVCC";
385             case TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED:
386                 return "RIL_CALL_LIST_CHANGED";
387             case TelephonyCallSession.Event.Type.IMS_COMMAND:
388                 return "IMS_COMMAND";
389             case TelephonyCallSession.Event.Type.IMS_COMMAND_RECEIVED:
390                 return "IMS_COMMAND_RECEIVED";
391             case TelephonyCallSession.Event.Type.IMS_COMMAND_FAILED:
392                 return "IMS_COMMAND_FAILED";
393             case TelephonyCallSession.Event.Type.IMS_COMMAND_COMPLETE:
394                 return "IMS_COMMAND_COMPLETE";
395             case TelephonyCallSession.Event.Type.IMS_CALL_RECEIVE:
396                 return "IMS_CALL_RECEIVE";
397             case TelephonyCallSession.Event.Type.IMS_CALL_STATE_CHANGED:
398                 return "IMS_CALL_STATE_CHANGED";
399             case TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED:
400                 return "IMS_CALL_TERMINATED";
401             case TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER:
402                 return "IMS_CALL_HANDOVER";
403             case TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER_FAILED:
404                 return "IMS_CALL_HANDOVER_FAILED";
405             case TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED:
406                 return "PHONE_STATE_CHANGED";
407             case TelephonyCallSession.Event.Type.NITZ_TIME:
408                 return "NITZ_TIME";
409             case TelephonyCallSession.Event.Type.AUDIO_CODEC:
410                 return "AUDIO_CODEC";
411             default:
412                 return Integer.toString(event);
413         }
414     }
415 
416     /**
417      * Convert the SMS session event into string
418      * @param event The event in integer
419      * @return The event in String
420      */
smsSessionEventToString(int event)421     private static String smsSessionEventToString(int event) {
422         switch (event) {
423             case SmsSession.Event.Type.EVENT_UNKNOWN:
424                 return "EVENT_UNKNOWN";
425             case SmsSession.Event.Type.SETTINGS_CHANGED:
426                 return "SETTINGS_CHANGED";
427             case SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED:
428                 return "RIL_SERVICE_STATE_CHANGED";
429             case SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED:
430                 return "IMS_CONNECTION_STATE_CHANGED";
431             case SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED:
432                 return "IMS_CAPABILITIES_CHANGED";
433             case SmsSession.Event.Type.DATA_CALL_LIST_CHANGED:
434                 return "DATA_CALL_LIST_CHANGED";
435             case SmsSession.Event.Type.SMS_SEND:
436                 return "SMS_SEND";
437             case SmsSession.Event.Type.SMS_SEND_RESULT:
438                 return "SMS_SEND_RESULT";
439             case SmsSession.Event.Type.SMS_RECEIVED:
440                 return "SMS_RECEIVED";
441             case SmsSession.Event.Type.INCOMPLETE_SMS_RECEIVED:
442                 return "INCOMPLETE_SMS_RECEIVED";
443             default:
444                 return Integer.toString(event);
445         }
446     }
447 
448     /**
449      * Print all metrics data for debugging purposes
450      *
451      * @param rawWriter Print writer
452      */
printAllMetrics(PrintWriter rawWriter)453     private synchronized void printAllMetrics(PrintWriter rawWriter) {
454         final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, "  ");
455 
456         pw.println("Telephony metrics proto:");
457         pw.println("------------------------------------------");
458         pw.println("Telephony events:");
459         pw.increaseIndent();
460         for (TelephonyEvent event : mTelephonyEvents) {
461             pw.print(event.timestampMillis);
462             pw.print(" [");
463             pw.print(event.phoneId);
464             pw.print("] ");
465 
466             pw.print("T=");
467             if (event.type == TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED) {
468                 pw.print(telephonyEventToString(event.type)
469                         + "(" + "Data RAT " + event.serviceState.dataRat
470                         + " Voice RAT " + event.serviceState.voiceRat
471                         + " Channel Number " + event.serviceState.channelNumber
472                         + " NR Frequency Range " + event.serviceState.nrFrequencyRange
473                         + " NR State " + event.serviceState.nrState
474                         + ")");
475                 for (int i = 0; i < event.serviceState.networkRegistrationInfo.length; i++) {
476                     pw.print("reg info: domain="
477                             + event.serviceState.networkRegistrationInfo[i].domain
478                             + ", rat=" + event.serviceState.networkRegistrationInfo[i].rat);
479                 }
480             } else {
481                 pw.print(telephonyEventToString(event.type));
482             }
483 
484             pw.println("");
485         }
486 
487         pw.decreaseIndent();
488         pw.println("Call sessions:");
489         pw.increaseIndent();
490 
491         for (TelephonyCallSession callSession : mCompletedCallSessions) {
492             pw.print("Start time in minutes: " + callSession.startTimeMinutes);
493             pw.print(", phone: " + callSession.phoneId);
494             if (callSession.eventsDropped) {
495                 pw.println(", events dropped: " + callSession.eventsDropped);
496             } else {
497                 pw.println("");
498             }
499 
500             pw.println("Events: ");
501             pw.increaseIndent();
502             for (TelephonyCallSession.Event event : callSession.events) {
503                 pw.print(event.delay);
504                 pw.print(" T=");
505                 if (event.type == TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) {
506                     pw.println(callSessionEventToString(event.type)
507                             + "(" + "Data RAT " + event.serviceState.dataRat
508                             + " Voice RAT " + event.serviceState.voiceRat
509                             + " Channel Number " + event.serviceState.channelNumber
510                             + " NR Frequency Range " + event.serviceState.nrFrequencyRange
511                             + " NR State " + event.serviceState.nrState
512                             + ")");
513                 } else if (event.type == TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED) {
514                     pw.println(callSessionEventToString(event.type));
515                     pw.increaseIndent();
516                     for (RilCall call : event.calls) {
517                         pw.println(call.index + ". Type = " + call.type + " State = "
518                                 + call.state + " End Reason " + call.callEndReason
519                                 + " Precise Disconnect Cause " + call.preciseDisconnectCause
520                                 + " isMultiparty = " + call.isMultiparty);
521                     }
522                     pw.decreaseIndent();
523                 } else if (event.type == TelephonyCallSession.Event.Type.AUDIO_CODEC) {
524                     pw.println(callSessionEventToString(event.type)
525                             + "(" + event.audioCodec + ")");
526                 } else {
527                     pw.println(callSessionEventToString(event.type));
528                 }
529             }
530             pw.decreaseIndent();
531         }
532 
533         pw.decreaseIndent();
534         pw.println("Sms sessions:");
535         pw.increaseIndent();
536 
537         int count = 0;
538         for (SmsSession smsSession : mCompletedSmsSessions) {
539             count++;
540             pw.print("[" + count + "] Start time in minutes: "
541                     + smsSession.startTimeMinutes);
542             pw.print(", phone: " + smsSession.phoneId);
543             if (smsSession.eventsDropped) {
544                 pw.println(", events dropped: " + smsSession.eventsDropped);
545             } else {
546                 pw.println("");
547             }
548             pw.println("Events: ");
549             pw.increaseIndent();
550             for (SmsSession.Event event : smsSession.events) {
551                 pw.print(event.delay);
552                 pw.print(" T=");
553                 if (event.type == SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED) {
554                     pw.println(smsSessionEventToString(event.type)
555                             + "(" + "Data RAT " + event.serviceState.dataRat
556                             + " Voice RAT " + event.serviceState.voiceRat
557                             + " Channel Number " + event.serviceState.channelNumber
558                             + " NR Frequency Range " + event.serviceState.nrFrequencyRange
559                             + " NR State " + event.serviceState.nrState
560                             + ")");
561                 } else if (event.type == SmsSession.Event.Type.SMS_RECEIVED) {
562                     pw.println(smsSessionEventToString(event.type));
563                     pw.increaseIndent();
564                     switch (event.smsType) {
565                         case SmsSession.Event.SmsType.SMS_TYPE_SMS_PP:
566                             pw.println("Type: SMS-PP");
567                             break;
568                         case SmsSession.Event.SmsType.SMS_TYPE_VOICEMAIL_INDICATION:
569                             pw.println("Type: Voicemail indication");
570                             break;
571                         case SmsSession.Event.SmsType.SMS_TYPE_ZERO:
572                             pw.println("Type: zero");
573                             break;
574                         case SmsSession.Event.SmsType.SMS_TYPE_WAP_PUSH:
575                             pw.println("Type: WAP PUSH");
576                             break;
577                         default:
578                             break;
579                     }
580                     if (event.errorCode != SmsManager.RESULT_ERROR_NONE) {
581                         pw.println("E=" + event.errorCode);
582                     }
583                     pw.decreaseIndent();
584                 } else if (event.type == SmsSession.Event.Type.SMS_SEND
585                         || event.type == SmsSession.Event.Type.SMS_SEND_RESULT) {
586                     pw.println(smsSessionEventToString(event.type));
587                     pw.increaseIndent();
588                     pw.println("ReqId=" + event.rilRequestId);
589                     pw.println("E=" + event.errorCode);
590                     pw.println("RilE=" + event.error);
591                     pw.println("ImsE=" + event.imsError);
592                     pw.decreaseIndent();
593                 } else if (event.type == SmsSession.Event.Type.INCOMPLETE_SMS_RECEIVED) {
594                     pw.println(smsSessionEventToString(event.type));
595                     pw.increaseIndent();
596                     pw.println("Received: " + event.incompleteSms.receivedParts + "/"
597                             + event.incompleteSms.totalParts);
598                     pw.decreaseIndent();
599                 }
600             }
601             pw.decreaseIndent();
602         }
603 
604         pw.decreaseIndent();
605         pw.println("Modem power stats:");
606         pw.increaseIndent();
607 
608         BatteryStatsManager batteryStatsManager = mContext == null ? null :
609                 (BatteryStatsManager) mContext.getSystemService(Context.BATTERY_STATS_SERVICE);
610         ModemPowerStats s = new ModemPowerMetrics(batteryStatsManager).buildProto();
611 
612         pw.println("Power log duration (battery time) (ms): " + s.loggingDurationMs);
613         pw.println("Energy consumed by modem (mAh): " + s.energyConsumedMah);
614         pw.println("Number of packets sent (tx): " + s.numPacketsTx);
615         pw.println("Number of bytes sent (tx): " + s.numBytesTx);
616         pw.println("Number of packets received (rx): " + s.numPacketsRx);
617         pw.println("Number of bytes received (rx): " + s.numBytesRx);
618         pw.println("Amount of time kernel is active because of cellular data (ms): "
619                 + s.cellularKernelActiveTimeMs);
620         pw.println("Amount of time spent in very poor rx signal level (ms): "
621                 + s.timeInVeryPoorRxSignalLevelMs);
622         pw.println("Amount of time modem is in sleep (ms): " + s.sleepTimeMs);
623         pw.println("Amount of time modem is in idle (ms): " + s.idleTimeMs);
624         pw.println("Amount of time modem is in rx (ms): " + s.rxTimeMs);
625         pw.println("Amount of time modem is in tx (ms): " + Arrays.toString(s.txTimeMs));
626         pw.println("Amount of time phone spent in various Radio Access Technologies (ms): "
627                 + Arrays.toString(s.timeInRatMs));
628         pw.println("Amount of time phone spent in various cellular "
629                 + "rx signal strength levels (ms): "
630                 + Arrays.toString(s.timeInRxSignalStrengthLevelMs));
631         pw.println("Energy consumed across measured modem rails (mAh): "
632                 + new DecimalFormat("#.##").format(s.monitoredRailEnergyConsumedMah));
633         pw.decreaseIndent();
634         pw.println("Hardware Version: " + SystemProperties.get("ro.boot.revision", ""));
635 
636         pw.decreaseIndent();
637         pw.println("LinkBandwidthEstimator stats:");
638         pw.increaseIndent();
639 
640         pw.println("Tx");
641         for (BwEstimationStats stats : mBwEstStatsMapList.get(0).values()) {
642             pw.println(stats.toString());
643         }
644 
645         pw.println("Rx");
646         for (BwEstimationStats stats : mBwEstStatsMapList.get(1).values()) {
647             pw.println(stats.toString());
648         }
649 
650         RcsStats.getInstance().printAllMetrics(rawWriter);
651     }
652 
653     /**
654      * Convert the telephony proto into Base-64 encoded string
655      *
656      * @param proto Telephony proto
657      * @return Encoded string
658      */
convertProtoToBase64String(TelephonyLog proto)659     private static String convertProtoToBase64String(TelephonyLog proto) {
660         return Base64.encodeToString(
661                 TelephonyProto.TelephonyLog.toByteArray(proto), Base64.DEFAULT);
662     }
663 
664     /**
665      * Reset all events and sessions
666      */
reset()667     private synchronized void reset() {
668         mTelephonyEvents.clear();
669         mCompletedCallSessions.clear();
670         mCompletedSmsSessions.clear();
671         mBwEstStatsMapList.get(0).clear();
672         mBwEstStatsMapList.get(1).clear();
673 
674         mTelephonyEventsDropped = false;
675 
676         mStartSystemTimeMs = System.currentTimeMillis();
677         mStartElapsedTimeMs = SystemClock.elapsedRealtime();
678 
679         // Insert the last known sim state, enabled modem bitmap, active subscription info,
680         // service state, ims capabilities, ims connection states, carrier id and Data call
681         // events as the base.
682         // Sim state, modem bitmap and active subscription info events are logged before
683         // other events.
684         addTelephonyEvent(new TelephonyEventBuilder(mStartElapsedTimeMs, -1 /* phoneId */)
685                 .setSimStateChange(mLastSimState).build());
686 
687         addTelephonyEvent(new TelephonyEventBuilder(mStartElapsedTimeMs, -1 /* phoneId */)
688                 .setEnabledModemBitmap(mLastEnabledModemBitmap).build());
689 
690         for (int i = 0; i < mLastActiveSubscriptionInfos.size(); i++) {
691           final int key = mLastActiveSubscriptionInfos.keyAt(i);
692           TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
693                   .setActiveSubscriptionInfoChange(mLastActiveSubscriptionInfos.get(key)).build();
694           addTelephonyEvent(event);
695         }
696 
697         for (int i = 0; i < mLastServiceState.size(); i++) {
698             final int key = mLastServiceState.keyAt(i);
699 
700             TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
701                     .setServiceState(mLastServiceState.get(key)).build();
702             addTelephonyEvent(event);
703         }
704 
705         for (int i = 0; i < mLastImsCapabilities.size(); i++) {
706             final int key = mLastImsCapabilities.keyAt(i);
707 
708             TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
709                     .setImsCapabilities(mLastImsCapabilities.get(key)).build();
710             addTelephonyEvent(event);
711         }
712 
713         for (int i = 0; i < mLastImsConnectionState.size(); i++) {
714             final int key = mLastImsConnectionState.keyAt(i);
715 
716             TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
717                     .setImsConnectionState(mLastImsConnectionState.get(key)).build();
718             addTelephonyEvent(event);
719         }
720 
721         for (int i = 0; i < mLastCarrierId.size(); i++) {
722             final int key = mLastCarrierId.keyAt(i);
723             TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
724                     .setCarrierIdMatching(mLastCarrierId.get(key)).build();
725             addTelephonyEvent(event);
726         }
727 
728         for (int i = 0; i < mLastNetworkCapabilitiesInfos.size(); i++) {
729             final int key = mLastNetworkCapabilitiesInfos.keyAt(i);
730             TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
731                     .setNetworkCapabilities(mLastNetworkCapabilitiesInfos.get(key)).build();
732             addTelephonyEvent(event);
733         }
734 
735         for (int i = 0; i < mLastRilDataCallEvents.size(); i++) {
736             final int key = mLastRilDataCallEvents.keyAt(i);
737             for (int j = 0; j < mLastRilDataCallEvents.get(key).size(); j++) {
738                 final int cidKey = mLastRilDataCallEvents.get(key).keyAt(j);
739                 RilDataCall[] dataCalls = new RilDataCall[1];
740                 dataCalls[0] = mLastRilDataCallEvents.get(key).get(cidKey);
741                 addTelephonyEvent(new TelephonyEventBuilder(mStartElapsedTimeMs, key)
742                         .setDataCalls(dataCalls).build());
743             }
744         }
745 
746         for (int i = 0; i < mLastRadioState.size(); i++) {
747             final int key = mLastRadioState.keyAt(i);
748             TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
749                     .setRadioState(mLastRadioState.get(key)).build();
750             addTelephonyEvent(event);
751         }
752 
753         RcsStats.getInstance().reset();
754     }
755 
756     /**
757      * Build the telephony proto
758      *
759      * @return Telephony proto
760      */
buildProto()761     private synchronized TelephonyLog buildProto() {
762 
763         TelephonyLog log = new TelephonyLog();
764         // Build telephony events
765         log.events = new TelephonyEvent[mTelephonyEvents.size()];
766         mTelephonyEvents.toArray(log.events);
767         log.eventsDropped = mTelephonyEventsDropped;
768 
769         // Build call sessions
770         log.callSessions = new TelephonyCallSession[mCompletedCallSessions.size()];
771         mCompletedCallSessions.toArray(log.callSessions);
772 
773         // Build SMS sessions
774         log.smsSessions = new SmsSession[mCompletedSmsSessions.size()];
775         mCompletedSmsSessions.toArray(log.smsSessions);
776 
777         // Build histogram. Currently we only support RIL histograms.
778         List<TelephonyHistogram> rilHistograms = RIL.getTelephonyRILTimingHistograms();
779         log.histograms = new TelephonyProto.TelephonyHistogram[rilHistograms.size()];
780         for (int i = 0; i < rilHistograms.size(); i++) {
781             log.histograms[i] = new TelephonyProto.TelephonyHistogram();
782             TelephonyHistogram rilHistogram = rilHistograms.get(i);
783             TelephonyProto.TelephonyHistogram histogramProto = log.histograms[i];
784 
785             histogramProto.category = rilHistogram.getCategory();
786             histogramProto.id = rilHistogram.getId();
787             histogramProto.minTimeMillis = rilHistogram.getMinTime();
788             histogramProto.maxTimeMillis = rilHistogram.getMaxTime();
789             histogramProto.avgTimeMillis = rilHistogram.getAverageTime();
790             histogramProto.count = rilHistogram.getSampleCount();
791             histogramProto.bucketCount = rilHistogram.getBucketCount();
792             histogramProto.bucketEndPoints = rilHistogram.getBucketEndPoints();
793             histogramProto.bucketCounters = rilHistogram.getBucketCounters();
794         }
795 
796         // Build modem power metrics
797         BatteryStatsManager batteryStatsManager = mContext == null ? null :
798                 (BatteryStatsManager) mContext.getSystemService(Context.BATTERY_STATS_SERVICE);
799         log.modemPowerStats = new ModemPowerMetrics(batteryStatsManager).buildProto();
800 
801         // Log the hardware revision
802         log.hardwareRevision = SystemProperties.get("ro.boot.revision", "");
803 
804         // Log the starting system time
805         log.startTime = new TelephonyProto.Time();
806         log.startTime.systemTimestampMillis = mStartSystemTimeMs;
807         log.startTime.elapsedTimestampMillis = mStartElapsedTimeMs;
808 
809         log.endTime = new TelephonyProto.Time();
810         log.endTime.systemTimestampMillis = System.currentTimeMillis();
811         log.endTime.elapsedTimestampMillis = SystemClock.elapsedRealtime();
812 
813         // Log the last active subscription information.
814         int phoneCount = TelephonyManager.getDefault().getPhoneCount();
815         ActiveSubscriptionInfo[] activeSubscriptionInfo =
816                 new ActiveSubscriptionInfo[phoneCount];
817         for (int i = 0; i < mLastActiveSubscriptionInfos.size(); i++) {
818             int key = mLastActiveSubscriptionInfos.keyAt(i);
819             activeSubscriptionInfo[key] = mLastActiveSubscriptionInfos.get(key);
820         }
821         for (int i = 0; i < phoneCount; i++) {
822             if (activeSubscriptionInfo[i] == null) {
823                 activeSubscriptionInfo[i] = makeInvalidSubscriptionInfo(i);
824             }
825         }
826         log.lastActiveSubscriptionInfo = activeSubscriptionInfo;
827         log.bandwidthEstimatorStats = buildBandwidthEstimatorStats();
828         return log;
829     }
830 
831     /** Update the sim state. */
updateSimState(int phoneId, int simState)832     public void updateSimState(int phoneId, int simState) {
833         int state = mapSimStateToProto(simState);
834         Integer lastSimState = mLastSimState.get(phoneId);
835         if (lastSimState == null || !lastSimState.equals(state)) {
836             mLastSimState.put(phoneId, state);
837             addTelephonyEvent(
838                     new TelephonyEventBuilder(phoneId).setSimStateChange(mLastSimState).build());
839         }
840     }
841 
842     /** Update active subscription info list. */
updateActiveSubscriptionInfoList(List<SubscriptionInfo> subInfos)843     public synchronized void updateActiveSubscriptionInfoList(List<SubscriptionInfo> subInfos) {
844         List<Integer> inActivePhoneList = new ArrayList<>();
845         for (int i = 0; i < mLastActiveSubscriptionInfos.size(); i++) {
846             inActivePhoneList.add(mLastActiveSubscriptionInfos.keyAt(i));
847         }
848 
849         for (SubscriptionInfo info : subInfos) {
850             int phoneId = info.getSimSlotIndex();
851             inActivePhoneList.removeIf(value -> value.equals(phoneId));
852             ActiveSubscriptionInfo activeSubscriptionInfo = new ActiveSubscriptionInfo();
853             activeSubscriptionInfo.slotIndex = phoneId;
854             activeSubscriptionInfo.isOpportunistic = info.isOpportunistic() ? 1 : 0;
855             activeSubscriptionInfo.carrierId = info.getCarrierId();
856             if (info.getMccString() != null && info.getMncString() != null) {
857                 activeSubscriptionInfo.simMccmnc = info.getMccString() + info.getMncString();
858             }
859             if (!MessageNano.messageNanoEquals(
860                     mLastActiveSubscriptionInfos.get(phoneId), activeSubscriptionInfo)) {
861                 addTelephonyEvent(new TelephonyEventBuilder(phoneId)
862                         .setActiveSubscriptionInfoChange(activeSubscriptionInfo).build());
863 
864                 mLastActiveSubscriptionInfos.put(phoneId, activeSubscriptionInfo);
865             }
866         }
867 
868         for (int phoneId : inActivePhoneList) {
869             mLastActiveSubscriptionInfos.remove(phoneId);
870             addTelephonyEvent(new TelephonyEventBuilder(phoneId)
871                     .setActiveSubscriptionInfoChange(makeInvalidSubscriptionInfo(phoneId)).build());
872         }
873     }
874 
875     /** Update the enabled modem bitmap. */
updateEnabledModemBitmap(int enabledModemBitmap)876     public void updateEnabledModemBitmap(int enabledModemBitmap) {
877         if (mLastEnabledModemBitmap == enabledModemBitmap) return;
878         mLastEnabledModemBitmap = enabledModemBitmap;
879         addTelephonyEvent(new TelephonyEventBuilder()
880                 .setEnabledModemBitmap(mLastEnabledModemBitmap).build());
881     }
882 
makeInvalidSubscriptionInfo(int phoneId)883     private static ActiveSubscriptionInfo makeInvalidSubscriptionInfo(int phoneId) {
884         ActiveSubscriptionInfo invalidSubscriptionInfo = new ActiveSubscriptionInfo();
885         invalidSubscriptionInfo.slotIndex = phoneId;
886         invalidSubscriptionInfo.carrierId = -1;
887         invalidSubscriptionInfo.isOpportunistic = -1;
888         return invalidSubscriptionInfo;
889     }
890 
891     /**
892      * Reduce precision to meet privacy requirements.
893      *
894      * @param timestamp timestamp in milliseconds
895      * @return Precision reduced timestamp in minutes
896      */
roundSessionStart(long timestamp)897     static int roundSessionStart(long timestamp) {
898         return (int) ((timestamp) / (MINUTE_IN_MILLIS * SESSION_START_PRECISION_MINUTES)
899                 * (SESSION_START_PRECISION_MINUTES));
900     }
901 
902     /**
903      * Write the Carrier Key change event
904      *
905      * @param phoneId Phone id
906      * @param keyType type of key
907      * @param isDownloadSuccessful true if the key was successfully downloaded
908      */
writeCarrierKeyEvent(int phoneId, int keyType, boolean isDownloadSuccessful)909     public void writeCarrierKeyEvent(int phoneId, int keyType,  boolean isDownloadSuccessful) {
910         final CarrierKeyChange carrierKeyChange = new CarrierKeyChange();
911         carrierKeyChange.keyType = keyType;
912         carrierKeyChange.isDownloadSuccessful = isDownloadSuccessful;
913         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setCarrierKeyChange(
914                 carrierKeyChange).build();
915         addTelephonyEvent(event);
916     }
917 
918 
919     /**
920      * Get the time interval with reduced prevision
921      *
922      * @param previousTimestamp Previous timestamp in milliseconds
923      * @param currentTimestamp Current timestamp in milliseconds
924      * @return The time interval
925      */
toPrivacyFuzzedTimeInterval(long previousTimestamp, long currentTimestamp)926     static int toPrivacyFuzzedTimeInterval(long previousTimestamp, long currentTimestamp) {
927         long diff = currentTimestamp - previousTimestamp;
928         if (diff < 0) {
929             return TimeInterval.TI_UNKNOWN;
930         } else if (diff <= 10) {
931             return TimeInterval.TI_10_MILLIS;
932         } else if (diff <= 20) {
933             return TimeInterval.TI_20_MILLIS;
934         } else if (diff <= 50) {
935             return TimeInterval.TI_50_MILLIS;
936         } else if (diff <= 100) {
937             return TimeInterval.TI_100_MILLIS;
938         } else if (diff <= 200) {
939             return TimeInterval.TI_200_MILLIS;
940         } else if (diff <= 500) {
941             return TimeInterval.TI_500_MILLIS;
942         } else if (diff <= 1000) {
943             return TimeInterval.TI_1_SEC;
944         } else if (diff <= 2000) {
945             return TimeInterval.TI_2_SEC;
946         } else if (diff <= 5000) {
947             return TimeInterval.TI_5_SEC;
948         } else if (diff <= 10000) {
949             return TimeInterval.TI_10_SEC;
950         } else if (diff <= 30000) {
951             return TimeInterval.TI_30_SEC;
952         } else if (diff <= 60000) {
953             return TimeInterval.TI_1_MINUTE;
954         } else if (diff <= 180000) {
955             return TimeInterval.TI_3_MINUTES;
956         } else if (diff <= 600000) {
957             return TimeInterval.TI_10_MINUTES;
958         } else if (diff <= 1800000) {
959             return TimeInterval.TI_30_MINUTES;
960         } else if (diff <= 3600000) {
961             return TimeInterval.TI_1_HOUR;
962         } else if (diff <= 7200000) {
963             return TimeInterval.TI_2_HOURS;
964         } else if (diff <= 14400000) {
965             return TimeInterval.TI_4_HOURS;
966         } else {
967             return TimeInterval.TI_MANY_HOURS;
968         }
969     }
970 
971     /**
972      * Convert the service state into service state proto
973      *
974      * @param serviceState Service state
975      * @return Service state proto
976      */
toServiceStateProto(ServiceState serviceState)977     private TelephonyServiceState toServiceStateProto(ServiceState serviceState) {
978         TelephonyServiceState ssProto = new TelephonyServiceState();
979 
980         ssProto.voiceRoamingType = serviceState.getVoiceRoamingType();
981         ssProto.dataRoamingType = serviceState.getDataRoamingType();
982 
983         ssProto.voiceOperator = new TelephonyServiceState.TelephonyOperator();
984         ssProto.dataOperator = new TelephonyServiceState.TelephonyOperator();
985         if (serviceState.getOperatorAlphaLong() != null) {
986             ssProto.voiceOperator.alphaLong = serviceState.getOperatorAlphaLong();
987             ssProto.dataOperator.alphaLong = serviceState.getOperatorAlphaLong();
988         }
989 
990         if (serviceState.getOperatorAlphaShort() != null) {
991             ssProto.voiceOperator.alphaShort = serviceState.getOperatorAlphaShort();
992             ssProto.dataOperator.alphaShort = serviceState.getOperatorAlphaShort();
993         }
994 
995         if (serviceState.getOperatorNumeric() != null) {
996             ssProto.voiceOperator.numeric = serviceState.getOperatorNumeric();
997             ssProto.dataOperator.numeric = serviceState.getOperatorNumeric();
998         }
999 
1000         // Log PS WWAN only because CS WWAN would be exactly the same as voiceRat, and PS WLAN
1001         // would be always IWLAN in the rat field.
1002         // Note that we intentionally do not log reg state because it changes too frequently that
1003         // will grow the proto size too much.
1004         List<TelephonyServiceState.NetworkRegistrationInfo> nriList = new ArrayList<>();
1005         NetworkRegistrationInfo nri = serviceState.getNetworkRegistrationInfo(
1006                 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
1007         if (nri != null) {
1008             TelephonyServiceState.NetworkRegistrationInfo nriProto =
1009                     new TelephonyServiceState.NetworkRegistrationInfo();
1010             nriProto.domain = TelephonyServiceState.Domain.DOMAIN_PS;
1011             nriProto.transport = TelephonyServiceState.Transport.TRANSPORT_WWAN;
1012             nriProto.rat = ServiceState.networkTypeToRilRadioTechnology(
1013                     nri.getAccessNetworkTechnology());
1014             nriList.add(nriProto);
1015             ssProto.networkRegistrationInfo =
1016                     new TelephonyServiceState.NetworkRegistrationInfo[nriList.size()];
1017             nriList.toArray(ssProto.networkRegistrationInfo);
1018         }
1019 
1020         ssProto.voiceRat = serviceState.getRilVoiceRadioTechnology();
1021         ssProto.dataRat = serviceState.getRilDataRadioTechnology();
1022         ssProto.channelNumber = serviceState.getChannelNumber();
1023         ssProto.nrFrequencyRange = serviceState.getNrFrequencyRange();
1024         ssProto.nrState = serviceState.getNrState();
1025         return ssProto;
1026     }
1027 
1028     /**
1029      * Annotate the call session with events
1030      *
1031      * @param timestamp Event timestamp
1032      * @param phoneId Phone id
1033      * @param eventBuilder Call session event builder
1034      */
annotateInProgressCallSession(long timestamp, int phoneId, CallSessionEventBuilder eventBuilder)1035     private synchronized void annotateInProgressCallSession(long timestamp, int phoneId,
1036                                                             CallSessionEventBuilder eventBuilder) {
1037         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1038         if (callSession != null) {
1039             callSession.addEvent(timestamp, eventBuilder);
1040         }
1041     }
1042 
1043     /**
1044      * Annotate the SMS session with events
1045      *
1046      * @param timestamp Event timestamp
1047      * @param phoneId Phone id
1048      * @param eventBuilder SMS session event builder
1049      */
annotateInProgressSmsSession(long timestamp, int phoneId, SmsSessionEventBuilder eventBuilder)1050     private synchronized void annotateInProgressSmsSession(long timestamp, int phoneId,
1051                                                            SmsSessionEventBuilder eventBuilder) {
1052         InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId);
1053         if (smsSession != null) {
1054             smsSession.addEvent(timestamp, eventBuilder);
1055         }
1056     }
1057 
1058     /**
1059      * Create the call session if there isn't any existing one
1060      *
1061      * @param phoneId Phone id
1062      * @return The call session
1063      */
startNewCallSessionIfNeeded(int phoneId)1064     private synchronized InProgressCallSession startNewCallSessionIfNeeded(int phoneId) {
1065         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1066         if (callSession == null) {
1067             logv("Starting a new call session on phone " + phoneId);
1068             callSession = new InProgressCallSession(phoneId);
1069             mInProgressCallSessions.append(phoneId, callSession);
1070 
1071             // Insert the latest service state, ims capabilities, and ims connection states as the
1072             // base.
1073             TelephonyServiceState serviceState = mLastServiceState.get(phoneId);
1074             if (serviceState != null) {
1075                 callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder(
1076                         TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
1077                         .setServiceState(serviceState));
1078             }
1079 
1080             ImsCapabilities imsCapabilities = mLastImsCapabilities.get(phoneId);
1081             if (imsCapabilities != null) {
1082                 callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder(
1083                         TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED)
1084                         .setImsCapabilities(imsCapabilities));
1085             }
1086 
1087             ImsConnectionState imsConnectionState = mLastImsConnectionState.get(phoneId);
1088             if (imsConnectionState != null) {
1089                 callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder(
1090                         TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
1091                         .setImsConnectionState(imsConnectionState));
1092             }
1093         }
1094         return callSession;
1095     }
1096 
1097     /**
1098      * Create the SMS session if there isn't any existing one
1099      *
1100      * @param phoneId Phone id
1101      * @return The SMS session
1102      */
startNewSmsSessionIfNeeded(int phoneId)1103     private synchronized InProgressSmsSession startNewSmsSessionIfNeeded(int phoneId) {
1104         InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId);
1105         if (smsSession == null) {
1106             logv("Starting a new sms session on phone " + phoneId);
1107             smsSession = startNewSmsSession(phoneId);
1108             mInProgressSmsSessions.append(phoneId, smsSession);
1109         }
1110         return smsSession;
1111     }
1112 
1113     /**
1114      * Create a new SMS session
1115      *
1116      * @param phoneId Phone id
1117      * @return The SMS session
1118      */
startNewSmsSession(int phoneId)1119     private InProgressSmsSession startNewSmsSession(int phoneId) {
1120         InProgressSmsSession smsSession = new InProgressSmsSession(phoneId);
1121 
1122         // Insert the latest service state, ims capabilities, and ims connection state as the
1123         // base.
1124         TelephonyServiceState serviceState = mLastServiceState.get(phoneId);
1125         if (serviceState != null) {
1126             smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder(
1127                     SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
1128                     .setServiceState(serviceState));
1129         }
1130 
1131         ImsCapabilities imsCapabilities = mLastImsCapabilities.get(phoneId);
1132         if (imsCapabilities != null) {
1133             smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder(
1134                     SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED)
1135                     .setImsCapabilities(imsCapabilities));
1136         }
1137 
1138         ImsConnectionState imsConnectionState = mLastImsConnectionState.get(phoneId);
1139         if (imsConnectionState != null) {
1140             smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder(
1141                     SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
1142                     .setImsConnectionState(imsConnectionState));
1143         }
1144         return smsSession;
1145     }
1146 
1147     /**
1148      * Finish the call session and move it into the completed session
1149      *
1150      * @param inProgressCallSession The in progress call session
1151      */
finishCallSession(InProgressCallSession inProgressCallSession)1152     private synchronized void finishCallSession(InProgressCallSession inProgressCallSession) {
1153         TelephonyCallSession callSession = new TelephonyCallSession();
1154         callSession.events = new TelephonyCallSession.Event[inProgressCallSession.events.size()];
1155         inProgressCallSession.events.toArray(callSession.events);
1156         callSession.startTimeMinutes = inProgressCallSession.startSystemTimeMin;
1157         callSession.phoneId = inProgressCallSession.phoneId;
1158         callSession.eventsDropped = inProgressCallSession.isEventsDropped();
1159         if (mCompletedCallSessions.size() >= MAX_COMPLETED_CALL_SESSIONS) {
1160             mCompletedCallSessions.removeFirst();
1161         }
1162         mCompletedCallSessions.add(callSession);
1163         mInProgressCallSessions.remove(inProgressCallSession.phoneId);
1164         logv("Call session finished");
1165     }
1166 
1167     /**
1168      * Finish the SMS session and move it into the completed session
1169      *
1170      * @param inProgressSmsSession The in progress SMS session
1171      */
finishSmsSessionIfNeeded(InProgressSmsSession inProgressSmsSession)1172     private synchronized void finishSmsSessionIfNeeded(InProgressSmsSession inProgressSmsSession) {
1173         if (inProgressSmsSession.getNumExpectedResponses() == 0) {
1174             SmsSession smsSession = finishSmsSession(inProgressSmsSession);
1175 
1176             mInProgressSmsSessions.remove(inProgressSmsSession.phoneId);
1177             logv("SMS session finished");
1178         }
1179     }
1180 
finishSmsSession(InProgressSmsSession inProgressSmsSession)1181     private synchronized SmsSession finishSmsSession(InProgressSmsSession inProgressSmsSession) {
1182         SmsSession smsSession = new SmsSession();
1183         smsSession.events = new SmsSession.Event[inProgressSmsSession.events.size()];
1184         inProgressSmsSession.events.toArray(smsSession.events);
1185         smsSession.startTimeMinutes = inProgressSmsSession.startSystemTimeMin;
1186         smsSession.phoneId = inProgressSmsSession.phoneId;
1187         smsSession.eventsDropped = inProgressSmsSession.isEventsDropped();
1188 
1189         if (mCompletedSmsSessions.size() >= MAX_COMPLETED_SMS_SESSIONS) {
1190             mCompletedSmsSessions.removeFirst();
1191         }
1192         mCompletedSmsSessions.add(smsSession);
1193         return smsSession;
1194     }
1195 
1196     /**
1197      * Add telephony event into the queue
1198      *
1199      * @param event Telephony event
1200      */
addTelephonyEvent(TelephonyEvent event)1201     private synchronized void addTelephonyEvent(TelephonyEvent event) {
1202         if (mTelephonyEvents.size() >= MAX_TELEPHONY_EVENTS) {
1203             mTelephonyEvents.removeFirst();
1204             mTelephonyEventsDropped = true;
1205         }
1206         mTelephonyEvents.add(event);
1207     }
1208 
1209     /**
1210      * Write service changed event
1211      *
1212      * @param phoneId Phone id
1213      * @param serviceState Service state
1214      */
writeServiceStateChanged(int phoneId, ServiceState serviceState)1215     public synchronized void writeServiceStateChanged(int phoneId, ServiceState serviceState) {
1216 
1217         TelephonyEvent event = new TelephonyEventBuilder(phoneId)
1218                 .setServiceState(toServiceStateProto(serviceState)).build();
1219 
1220         // If service state doesn't change, we don't log the event.
1221         if (mLastServiceState.get(phoneId) != null &&
1222                 Arrays.equals(TelephonyServiceState.toByteArray(mLastServiceState.get(phoneId)),
1223                         TelephonyServiceState.toByteArray(event.serviceState))) {
1224             return;
1225         }
1226 
1227         mLastServiceState.put(phoneId, event.serviceState);
1228         addTelephonyEvent(event);
1229 
1230         annotateInProgressCallSession(event.timestampMillis, phoneId,
1231                 new CallSessionEventBuilder(
1232                         TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
1233                         .setServiceState(event.serviceState));
1234         annotateInProgressSmsSession(event.timestampMillis, phoneId,
1235                 new SmsSessionEventBuilder(
1236                         SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
1237                         .setServiceState(event.serviceState));
1238     }
1239 
1240     /**
1241      * Write data stall event
1242      *
1243      * @param phoneId Phone id
1244      * @param recoveryAction Data stall recovery action
1245      */
writeDataStallEvent(int phoneId, int recoveryAction)1246     public void writeDataStallEvent(int phoneId, int recoveryAction) {
1247         addTelephonyEvent(new TelephonyEventBuilder(phoneId)
1248                 .setDataStallRecoveryAction(recoveryAction).build());
1249     }
1250 
1251     /**
1252      * Write SignalStrength event
1253      *
1254      * @param phoneId Phone id
1255      * @param signalStrength Signal strength at the time of data stall recovery
1256      */
writeSignalStrengthEvent(int phoneId, int signalStrength)1257     public void writeSignalStrengthEvent(int phoneId, int signalStrength) {
1258         addTelephonyEvent(new TelephonyEventBuilder(phoneId)
1259                 .setSignalStrength(signalStrength).build());
1260     }
1261 
cloneCurrentTelephonySettings(int phoneId)1262     private TelephonySettings cloneCurrentTelephonySettings(int phoneId) {
1263         TelephonySettings newSettings = new TelephonySettings();
1264         TelephonySettings lastSettings = mLastSettings.get(phoneId);
1265         if (lastSettings != null) {
1266             // No clone method available, so each relevant field is copied individually.
1267             newSettings.preferredNetworkMode = lastSettings.preferredNetworkMode;
1268             newSettings.isEnhanced4GLteModeEnabled = lastSettings.isEnhanced4GLteModeEnabled;
1269             newSettings.isVtOverLteEnabled = lastSettings.isVtOverLteEnabled;
1270             newSettings.isWifiCallingEnabled = lastSettings.isWifiCallingEnabled;
1271             newSettings.isVtOverWifiEnabled = lastSettings.isVtOverWifiEnabled;
1272         }
1273         return newSettings;
1274     }
1275 
1276     /**
1277      * Write IMS feature settings changed event
1278      *
1279      * @param phoneId Phone id
1280      * @param feature IMS feature
1281      * @param network The IMS network type
1282      * @param value The settings. 0 indicates disabled, otherwise enabled.
1283      */
writeImsSetFeatureValue(int phoneId, int feature, int network, int value)1284     public synchronized void writeImsSetFeatureValue(int phoneId, int feature, int network,
1285             int value) {
1286         TelephonySettings s = cloneCurrentTelephonySettings(phoneId);
1287         if (network == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
1288             switch (feature) {
1289                 case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE:
1290                     s.isEnhanced4GLteModeEnabled = (value != 0);
1291                     break;
1292                 case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
1293                     s.isVtOverLteEnabled = (value != 0);
1294                     break;
1295             }
1296         } else if (network == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
1297             switch (feature) {
1298                 case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE:
1299                     s.isWifiCallingEnabled = (value != 0);
1300                     break;
1301                 case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO:
1302                     s.isVtOverWifiEnabled = (value != 0);
1303                     break;
1304             }
1305         }
1306 
1307         // If the settings don't change, we don't log the event.
1308         if (mLastSettings.get(phoneId) != null &&
1309                 Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)),
1310                         TelephonySettings.toByteArray(s))) {
1311             return;
1312         }
1313 
1314         mLastSettings.put(phoneId, s);
1315 
1316         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setSettings(s).build();
1317         addTelephonyEvent(event);
1318 
1319         annotateInProgressCallSession(event.timestampMillis, phoneId,
1320                 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.SETTINGS_CHANGED)
1321                         .setSettings(s));
1322         annotateInProgressSmsSession(event.timestampMillis, phoneId,
1323                 new SmsSessionEventBuilder(SmsSession.Event.Type.SETTINGS_CHANGED)
1324                         .setSettings(s));
1325     }
1326 
1327     /**
1328      * Write the preferred network settings changed event
1329      *
1330      * @param phoneId Phone id
1331      * @param networkType The preferred network
1332      */
writeSetPreferredNetworkType(int phoneId, @PrefNetworkMode int networkType)1333     public synchronized void writeSetPreferredNetworkType(int phoneId,
1334             @PrefNetworkMode int networkType) {
1335         TelephonySettings s = cloneCurrentTelephonySettings(phoneId);
1336         s.preferredNetworkMode = networkType + 1;
1337 
1338         // If the settings don't change, we don't log the event.
1339         if (mLastSettings.get(phoneId) != null &&
1340                 Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)),
1341                         TelephonySettings.toByteArray(s))) {
1342             return;
1343         }
1344 
1345         mLastSettings.put(phoneId, s);
1346 
1347         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSettings(s).build());
1348     }
1349 
1350     /**
1351      * Write the IMS connection state changed event
1352      *
1353      * @param phoneId Phone id
1354      * @param state IMS connection state
1355      * @param reasonInfo The reason info. Only used for disconnected state.
1356      */
writeOnImsConnectionState(int phoneId, int state, ImsReasonInfo reasonInfo)1357     public synchronized void writeOnImsConnectionState(int phoneId, int state,
1358                                                        ImsReasonInfo reasonInfo) {
1359         ImsConnectionState imsState = new ImsConnectionState();
1360         imsState.state = state;
1361 
1362         if (reasonInfo != null) {
1363             TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo();
1364 
1365             ri.reasonCode = reasonInfo.getCode();
1366             ri.extraCode = reasonInfo.getExtraCode();
1367             String extraMessage = reasonInfo.getExtraMessage();
1368             if (extraMessage != null) {
1369                 ri.extraMessage = extraMessage;
1370             }
1371 
1372             imsState.reasonInfo = ri;
1373         }
1374 
1375         // If the connection state does not change, do not log it.
1376         if (mLastImsConnectionState.get(phoneId) != null &&
1377                 Arrays.equals(ImsConnectionState.toByteArray(mLastImsConnectionState.get(phoneId)),
1378                         ImsConnectionState.toByteArray(imsState))) {
1379             return;
1380         }
1381 
1382         mLastImsConnectionState.put(phoneId, imsState);
1383 
1384         TelephonyEvent event = new TelephonyEventBuilder(phoneId)
1385                 .setImsConnectionState(imsState).build();
1386         addTelephonyEvent(event);
1387 
1388         annotateInProgressCallSession(event.timestampMillis, phoneId,
1389                 new CallSessionEventBuilder(
1390                         TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
1391                         .setImsConnectionState(event.imsConnectionState));
1392         annotateInProgressSmsSession(event.timestampMillis, phoneId,
1393                 new SmsSessionEventBuilder(
1394                         SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
1395                         .setImsConnectionState(event.imsConnectionState));
1396     }
1397 
1398     /**
1399      * Write the IMS capabilities changed event
1400      *
1401      * @param phoneId Phone id
1402      * @param capabilities IMS capabilities array
1403      */
writeOnImsCapabilities(int phoneId, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech, MmTelFeature.MmTelCapabilities capabilities)1404     public synchronized void writeOnImsCapabilities(int phoneId,
1405             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech,
1406             MmTelFeature.MmTelCapabilities capabilities) {
1407         ImsCapabilities cap = new ImsCapabilities();
1408 
1409         if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
1410             cap.voiceOverLte = capabilities.isCapable(
1411                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
1412             cap.videoOverLte = capabilities.isCapable(
1413                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
1414             cap.utOverLte = capabilities.isCapable(
1415                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
1416 
1417         } else if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
1418             cap.voiceOverWifi = capabilities.isCapable(
1419                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
1420             cap.videoOverWifi = capabilities.isCapable(
1421                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
1422             cap.utOverWifi = capabilities.isCapable(
1423                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
1424         }
1425 
1426         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setImsCapabilities(cap).build();
1427 
1428         // If the capabilities don't change, we don't log the event.
1429         if (mLastImsCapabilities.get(phoneId) != null &&
1430                 Arrays.equals(ImsCapabilities.toByteArray(mLastImsCapabilities.get(phoneId)),
1431                 ImsCapabilities.toByteArray(cap))) {
1432             return;
1433         }
1434 
1435         mLastImsCapabilities.put(phoneId, cap);
1436         addTelephonyEvent(event);
1437 
1438         annotateInProgressCallSession(event.timestampMillis, phoneId,
1439                 new CallSessionEventBuilder(
1440                         TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED)
1441                         .setImsCapabilities(event.imsCapabilities));
1442         annotateInProgressSmsSession(event.timestampMillis, phoneId,
1443                 new SmsSessionEventBuilder(
1444                         SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED)
1445                         .setImsCapabilities(event.imsCapabilities));
1446     }
1447 
1448     /**
1449      * Convert PDP type into the enumeration
1450      *
1451      * @param type PDP type
1452      * @return The proto defined enumeration
1453      */
toPdpType(String type)1454     private int toPdpType(String type) {
1455         switch (type) {
1456             case "IP":
1457                 return PDP_TYPE_IP;
1458             case "IPV6":
1459                 return PDP_TYPE_IPV6;
1460             case "IPV4V6":
1461                 return PDP_TYPE_IPV4V6;
1462             case "PPP":
1463                 return PDP_TYPE_PPP;
1464             case "NON-IP":
1465                 return PDP_TYPE_NON_IP;
1466             case "UNSTRUCTURED":
1467                 return PDP_TYPE_UNSTRUCTURED;
1468         }
1469         Rlog.e(TAG, "Unknown type: " + type);
1470         return PDP_UNKNOWN;
1471     }
1472 
1473     /**
1474      * Write setup data call event
1475      *
1476      * @param phoneId Phone id
1477      * @param radioTechnology The data call RAT
1478      * @param profileId Data profile id
1479      * @param apn APN in string
1480      * @param protocol Data connection protocol
1481      */
writeSetupDataCall(int phoneId, int radioTechnology, int profileId, String apn, int protocol)1482     public void writeSetupDataCall(int phoneId, int radioTechnology, int profileId, String apn,
1483                                    int protocol) {
1484 
1485         RilSetupDataCall setupDataCall = new RilSetupDataCall();
1486         setupDataCall.rat = radioTechnology;
1487         setupDataCall.dataProfile = profileId + 1;  // off by 1 between proto and RIL constants.
1488         if (apn != null) {
1489             setupDataCall.apn = apn;
1490         }
1491 
1492         setupDataCall.type = protocol + 1;
1493 
1494         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSetupDataCall(
1495                 setupDataCall).build());
1496     }
1497 
1498     /**
1499      * Write data call deactivate event
1500      *
1501      * @param phoneId Phone id
1502      * @param rilSerial RIL request serial number
1503      * @param cid call id
1504      * @param reason Deactivate reason
1505      */
writeRilDeactivateDataCall(int phoneId, int rilSerial, int cid, int reason)1506     public void writeRilDeactivateDataCall(int phoneId, int rilSerial, int cid, int reason) {
1507 
1508         RilDeactivateDataCall deactivateDataCall = new RilDeactivateDataCall();
1509         deactivateDataCall.cid = cid;
1510         switch (reason) {
1511             case DataService.REQUEST_REASON_NORMAL:
1512                 deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_NONE;
1513                 break;
1514             case DataService.REQUEST_REASON_SHUTDOWN:
1515                 deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_RADIO_OFF;
1516                 break;
1517             case DataService.REQUEST_REASON_HANDOVER:
1518                 deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_HANDOVER;
1519                 break;
1520             default:
1521                 deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_UNKNOWN;
1522         }
1523 
1524         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDeactivateDataCall(
1525                 deactivateDataCall).build());
1526     }
1527 
1528     /**
1529      * Write data call list event when connected
1530      * @param phoneId          Phone id
1531      * @param cid              Context Id, uniquely identifies the call
1532      * @param apnTypeBitmask   Bitmask of supported APN types
1533      * @param state            State of the data call event
1534      */
writeRilDataCallEvent(int phoneId, int cid, int apnTypeBitmask, int state)1535     public void writeRilDataCallEvent(int phoneId, int cid,
1536             int apnTypeBitmask, int state) {
1537         RilDataCall[] dataCalls = new RilDataCall[1];
1538         dataCalls[0] = new RilDataCall();
1539         dataCalls[0].cid = cid;
1540         dataCalls[0].apnTypeBitmask = apnTypeBitmask;
1541         dataCalls[0].state = state;
1542 
1543         SparseArray<RilDataCall> dataCallList;
1544         if (mLastRilDataCallEvents.get(phoneId) != null) {
1545             // If the Data call event does not change, do not log it.
1546             if (mLastRilDataCallEvents.get(phoneId).get(cid) != null
1547                     && Arrays.equals(
1548                         RilDataCall.toByteArray(mLastRilDataCallEvents.get(phoneId).get(cid)),
1549                         RilDataCall.toByteArray(dataCalls[0]))) {
1550                 return;
1551             }
1552             dataCallList =  mLastRilDataCallEvents.get(phoneId);
1553         } else {
1554             dataCallList = new SparseArray<>();
1555         }
1556 
1557         dataCallList.put(cid, dataCalls[0]);
1558         mLastRilDataCallEvents.put(phoneId, dataCallList);
1559         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDataCalls(dataCalls).build());
1560     }
1561 
1562     /**
1563      * Write CS call list event
1564      *
1565      * @param phoneId    Phone id
1566      * @param connections Array of GsmCdmaConnection objects
1567      */
writeRilCallList(int phoneId, ArrayList<GsmCdmaConnection> connections, String countryIso)1568     public void writeRilCallList(int phoneId, ArrayList<GsmCdmaConnection> connections,
1569                                  String countryIso) {
1570         logv("Logging CallList Changed Connections Size = " + connections.size());
1571         InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
1572         if (callSession == null) {
1573             Rlog.e(TAG, "writeRilCallList: Call session is missing");
1574         } else {
1575             RilCall[] calls = convertConnectionsToRilCalls(connections, countryIso);
1576             callSession.addEvent(
1577                     new CallSessionEventBuilder(
1578                             TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED)
1579                             .setRilCalls(calls)
1580             );
1581             logv("Logged Call list changed");
1582             if (callSession.isPhoneIdle() && disconnectReasonsKnown(calls)) {
1583                 finishCallSession(callSession);
1584             }
1585         }
1586     }
1587 
disconnectReasonsKnown(RilCall[] calls)1588     private boolean disconnectReasonsKnown(RilCall[] calls) {
1589         for (RilCall call : calls) {
1590             if (call.callEndReason == 0) return false;
1591         }
1592         return true;
1593     }
1594 
convertConnectionsToRilCalls(ArrayList<GsmCdmaConnection> mConnections, String countryIso)1595     private RilCall[] convertConnectionsToRilCalls(ArrayList<GsmCdmaConnection> mConnections,
1596                                                    String countryIso) {
1597         RilCall[] calls = new RilCall[mConnections.size()];
1598         for (int i = 0; i < mConnections.size(); i++) {
1599             calls[i] = new RilCall();
1600             calls[i].index = i;
1601             convertConnectionToRilCall(mConnections.get(i), calls[i], countryIso);
1602         }
1603         return calls;
1604     }
1605 
convertEmergencyNumberToEmergencyNumberInfo(EmergencyNumber num)1606     private EmergencyNumberInfo convertEmergencyNumberToEmergencyNumberInfo(EmergencyNumber num) {
1607         EmergencyNumberInfo emergencyNumberInfo = new EmergencyNumberInfo();
1608         emergencyNumberInfo.address = num.getNumber();
1609         emergencyNumberInfo.countryIso = num.getCountryIso();
1610         emergencyNumberInfo.mnc = num.getMnc();
1611         emergencyNumberInfo.serviceCategoriesBitmask = num.getEmergencyServiceCategoryBitmask();
1612         emergencyNumberInfo.urns = num.getEmergencyUrns().stream().toArray(String[]::new);
1613         emergencyNumberInfo.numberSourcesBitmask = num.getEmergencyNumberSourceBitmask();
1614         emergencyNumberInfo.routing = num.getEmergencyCallRouting();
1615         return emergencyNumberInfo;
1616     }
1617 
convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call, String countryIso)1618     private void convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call,
1619                                             String countryIso) {
1620         if (conn.isIncoming()) {
1621             call.type = Type.MT;
1622         } else {
1623             call.type = Type.MO;
1624         }
1625         switch (conn.getState()) {
1626             case IDLE:
1627                 call.state = CallState.CALL_IDLE;
1628                 break;
1629             case ACTIVE:
1630                 call.state = CallState.CALL_ACTIVE;
1631                 break;
1632             case HOLDING:
1633                 call.state = CallState.CALL_HOLDING;
1634                 break;
1635             case DIALING:
1636                 call.state = CallState.CALL_DIALING;
1637                 break;
1638             case ALERTING:
1639                 call.state = CallState.CALL_ALERTING;
1640                 break;
1641             case INCOMING:
1642                 call.state = CallState.CALL_INCOMING;
1643                 break;
1644             case WAITING:
1645                 call.state = CallState.CALL_WAITING;
1646                 break;
1647             case DISCONNECTED:
1648                 call.state = CallState.CALL_DISCONNECTED;
1649                 break;
1650             case DISCONNECTING:
1651                 call.state = CallState.CALL_DISCONNECTING;
1652                 break;
1653             default:
1654                 call.state = CallState.CALL_UNKNOWN;
1655                 break;
1656         }
1657         call.callEndReason = conn.getDisconnectCause();
1658         call.isMultiparty = conn.isMultiparty();
1659         call.preciseDisconnectCause = conn.getPreciseDisconnectCause();
1660 
1661         // Emergency call metrics when call ends
1662         if (conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED
1663                 && conn.isEmergencyCall() && conn.getEmergencyNumberInfo() != null) {
1664             /** Only collect this emergency number information per sample percentage */
1665             if (ThreadLocalRandom.current().nextDouble(0, 100)
1666                     < getSamplePercentageForEmergencyCall(countryIso)) {
1667                 call.isEmergencyCall = conn.isEmergencyCall();
1668                 call.emergencyNumberInfo = convertEmergencyNumberToEmergencyNumberInfo(
1669                         conn.getEmergencyNumberInfo());
1670                 EmergencyNumberTracker emergencyNumberTracker = conn.getEmergencyNumberTracker();
1671                 call.emergencyNumberDatabaseVersion = emergencyNumberTracker != null
1672                         ? emergencyNumberTracker.getEmergencyNumberDbVersion()
1673                         : TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION;
1674             }
1675         }
1676     }
1677 
1678     /**
1679      * Write dial event
1680      *
1681      * @param phoneId Phone id
1682      * @param conn Connection object created to track this call
1683      * @param clirMode CLIR (Calling Line Identification Restriction) mode
1684      * @param uusInfo User-to-User signaling Info
1685      */
writeRilDial(int phoneId, GsmCdmaConnection conn, int clirMode, UUSInfo uusInfo)1686     public void writeRilDial(int phoneId, GsmCdmaConnection conn, int clirMode, UUSInfo uusInfo) {
1687 
1688         InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
1689         logv("Logging Dial Connection = " + conn);
1690         if (callSession == null) {
1691             Rlog.e(TAG, "writeRilDial: Call session is missing");
1692         } else {
1693             RilCall[] calls = new RilCall[1];
1694             calls[0] = new RilCall();
1695             calls[0].index = -1;
1696             convertConnectionToRilCall(conn, calls[0], "");
1697             callSession.addEvent(callSession.startElapsedTimeMs,
1698                     new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
1699                             .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL)
1700                             .setRilCalls(calls));
1701             logv("Logged Dial event");
1702         }
1703     }
1704 
1705     /**
1706      * Write incoming call event
1707      *
1708      * @param phoneId Phone id
1709      * @param response Unused today
1710      */
writeRilCallRing(int phoneId, char[] response)1711     public void writeRilCallRing(int phoneId, char[] response) {
1712         InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
1713 
1714         callSession.addEvent(callSession.startElapsedTimeMs,
1715                 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_CALL_RING));
1716     }
1717 
1718     /**
1719      * Write call hangup event
1720      *
1721      * @param phoneId Phone id
1722      * @param conn Connection object associated with the call that is being hung-up
1723      * @param callId Call id
1724      */
writeRilHangup(int phoneId, GsmCdmaConnection conn, int callId, String countryIso)1725     public void writeRilHangup(int phoneId, GsmCdmaConnection conn, int callId,
1726                                String countryIso) {
1727         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1728         if (callSession == null) {
1729             Rlog.e(TAG, "writeRilHangup: Call session is missing");
1730         } else {
1731             RilCall[] calls = new RilCall[1];
1732             calls[0] = new RilCall();
1733             calls[0].index = callId;
1734             convertConnectionToRilCall(conn, calls[0], countryIso);
1735             callSession.addEvent(
1736                     new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
1737                             .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP)
1738                             .setRilCalls(calls));
1739             logv("Logged Hangup event");
1740         }
1741     }
1742 
1743     /**
1744      * Write call answer event
1745      *
1746      * @param phoneId Phone id
1747      * @param rilSerial RIL request serial number
1748      */
writeRilAnswer(int phoneId, int rilSerial)1749     public void writeRilAnswer(int phoneId, int rilSerial) {
1750         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1751         if (callSession == null) {
1752             Rlog.e(TAG, "writeRilAnswer: Call session is missing");
1753         } else {
1754             callSession.addEvent(
1755                     new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
1756                             .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_ANSWER)
1757                             .setRilRequestId(rilSerial));
1758         }
1759     }
1760 
1761     /**
1762      * Write IMS call SRVCC event
1763      *
1764      * @param phoneId Phone id
1765      * @param rilSrvccState SRVCC state
1766      */
writeRilSrvcc(int phoneId, int rilSrvccState)1767     public void writeRilSrvcc(int phoneId, int rilSrvccState) {
1768         InProgressCallSession callSession =  mInProgressCallSessions.get(phoneId);
1769         if (callSession == null) {
1770             Rlog.e(TAG, "writeRilSrvcc: Call session is missing");
1771         } else {
1772             callSession.addEvent(
1773                     new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_CALL_SRVCC)
1774                             .setSrvccState(rilSrvccState + 1));
1775         }
1776     }
1777 
1778     /**
1779      * Convert RIL request into proto defined RIL request
1780      *
1781      * @param r RIL request
1782      * @return RIL request defined in call session proto
1783      */
toCallSessionRilRequest(int r)1784     private int toCallSessionRilRequest(int r) {
1785         switch (r) {
1786             case RILConstants.RIL_REQUEST_DIAL:
1787                 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL;
1788 
1789             case RILConstants.RIL_REQUEST_ANSWER:
1790                 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_ANSWER;
1791 
1792             case RILConstants.RIL_REQUEST_HANGUP:
1793             case RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
1794             case RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
1795                 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP;
1796 
1797             case RILConstants.RIL_REQUEST_SET_CALL_WAITING:
1798                 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_SET_CALL_WAITING;
1799 
1800             case RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE:
1801                 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE;
1802 
1803             case RILConstants.RIL_REQUEST_CDMA_FLASH:
1804                 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_CDMA_FLASH;
1805 
1806             case RILConstants.RIL_REQUEST_CONFERENCE:
1807                 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_CONFERENCE;
1808         }
1809         Rlog.e(TAG, "Unknown RIL request: " + r);
1810         return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_UNKNOWN;
1811     }
1812 
1813     /**
1814      * Write setup data call response event
1815      *
1816      * @param phoneId Phone id
1817      * @param rilSerial RIL request serial number
1818      * @param rilError RIL error
1819      * @param rilRequest RIL request
1820      * @param result Data call result
1821      */
writeOnSetupDataCallResponse(int phoneId, int rilSerial, int rilError, int rilRequest, DataCallResponse response)1822     private void writeOnSetupDataCallResponse(int phoneId, int rilSerial, int rilError,
1823                                               int rilRequest, DataCallResponse response) {
1824 
1825         RilSetupDataCallResponse setupDataCallResponse = new RilSetupDataCallResponse();
1826         RilDataCall dataCall = new RilDataCall();
1827 
1828         if (response != null) {
1829             setupDataCallResponse.status = (response.getCause() == 0
1830                     ? RilDataCallFailCause.PDP_FAIL_NONE : response.getCause());
1831             setupDataCallResponse.suggestedRetryTimeMillis = response.getSuggestedRetryTime();
1832 
1833             dataCall.cid = response.getId();
1834             dataCall.type = response.getProtocolType() + 1;
1835 
1836             if (!TextUtils.isEmpty(response.getInterfaceName())) {
1837                 dataCall.ifname = response.getInterfaceName();
1838             }
1839         }
1840         setupDataCallResponse.call = dataCall;
1841 
1842         addTelephonyEvent(new TelephonyEventBuilder(phoneId)
1843                 .setSetupDataCallResponse(setupDataCallResponse).build());
1844     }
1845 
1846     /**
1847      * Write call related solicited response event
1848      *
1849      * @param phoneId Phone id
1850      * @param rilSerial RIL request serial number
1851      * @param rilError RIL error
1852      * @param rilRequest RIL request
1853      */
writeOnCallSolicitedResponse(int phoneId, int rilSerial, int rilError, int rilRequest)1854     private void writeOnCallSolicitedResponse(int phoneId, int rilSerial, int rilError,
1855                                               int rilRequest) {
1856         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1857         if (callSession == null) {
1858             Rlog.e(TAG, "writeOnCallSolicitedResponse: Call session is missing");
1859         } else {
1860             callSession.addEvent(new CallSessionEventBuilder(
1861                     TelephonyCallSession.Event.Type.RIL_RESPONSE)
1862                     .setRilRequest(toCallSessionRilRequest(rilRequest))
1863                     .setRilRequestId(rilSerial)
1864                     .setRilError(rilError + 1));
1865         }
1866     }
1867 
1868     /**
1869      * Write SMS related solicited response event
1870      *
1871      * @param phoneId Phone id
1872      * @param rilSerial RIL request serial number
1873      * @param rilError RIL error
1874      * @param response SMS response
1875      */
writeOnSmsSolicitedResponse(int phoneId, int rilSerial, int rilError, SmsResponse response)1876     private synchronized void writeOnSmsSolicitedResponse(int phoneId, int rilSerial, int rilError,
1877                                                           SmsResponse response) {
1878         InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId);
1879         if (smsSession == null) {
1880             Rlog.e(TAG, "SMS session is missing");
1881         } else {
1882             int errorCode = SmsResponse.NO_ERROR_CODE;
1883             long messageId = 0L;
1884             if (response != null) {
1885                 errorCode = response.mErrorCode;
1886                 messageId = response.mMessageId;
1887             }
1888 
1889             smsSession.addEvent(new SmsSessionEventBuilder(
1890                     SmsSession.Event.Type.SMS_SEND_RESULT)
1891                     .setErrorCode(errorCode)
1892                     .setRilErrno(rilError + 1)
1893                     .setRilRequestId(rilSerial)
1894                     .setMessageId(messageId)
1895             );
1896 
1897             smsSession.decreaseExpectedResponse();
1898             finishSmsSessionIfNeeded(smsSession);
1899         }
1900     }
1901 
1902     /**
1903      * Write SMS related solicited response event
1904      *
1905      * @param phoneId Phone id
1906      * @param errorReason Defined in {@link SmsManager} RESULT_XXX.
1907      * @param messageId Unique id for this message.
1908      */
writeOnImsServiceSmsSolicitedResponse(int phoneId, @ImsSmsImplBase.SendStatusResult int resultCode, int errorReason, long messageId)1909     public synchronized void writeOnImsServiceSmsSolicitedResponse(int phoneId,
1910             @ImsSmsImplBase.SendStatusResult int resultCode, int errorReason,
1911             long messageId) {
1912 
1913         InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId);
1914         if (smsSession == null) {
1915             Rlog.e(TAG, "SMS session is missing");
1916         } else {
1917 
1918             smsSession.addEvent(new SmsSessionEventBuilder(
1919                     SmsSession.Event.Type.SMS_SEND_RESULT)
1920                     .setImsServiceErrno(resultCode)
1921                     .setErrorCode(errorReason)
1922                     .setMessageId(messageId)
1923             );
1924 
1925             smsSession.decreaseExpectedResponse();
1926             finishSmsSessionIfNeeded(smsSession);
1927         }
1928     }
1929 
1930     /**
1931      * Write deactivate data call response event
1932      *
1933      * @param phoneId Phone id
1934      * @param rilError RIL error
1935      */
writeOnDeactivateDataCallResponse(int phoneId, int rilError)1936     private void writeOnDeactivateDataCallResponse(int phoneId, int rilError) {
1937         addTelephonyEvent(new TelephonyEventBuilder(phoneId)
1938                 .setDeactivateDataCallResponse(rilError + 1).build());
1939     }
1940 
1941     /**
1942      * Write RIL solicited response event
1943      *
1944      * @param phoneId Phone id
1945      * @param rilSerial RIL request serial number
1946      * @param rilError RIL error
1947      * @param rilRequest RIL request
1948      * @param ret The returned RIL response
1949      */
writeOnRilSolicitedResponse(int phoneId, int rilSerial, int rilError, int rilRequest, Object ret)1950     public void writeOnRilSolicitedResponse(int phoneId, int rilSerial, int rilError,
1951                                             int rilRequest, Object ret) {
1952         switch (rilRequest) {
1953             case RIL_REQUEST_SETUP_DATA_CALL:
1954                 DataCallResponse response = (DataCallResponse) ret;
1955                 writeOnSetupDataCallResponse(phoneId, rilSerial, rilError, rilRequest, response);
1956                 break;
1957             case RIL_REQUEST_DEACTIVATE_DATA_CALL:
1958                 writeOnDeactivateDataCallResponse(phoneId, rilError);
1959                 break;
1960             case RIL_REQUEST_HANGUP:
1961             case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
1962             case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
1963             case RIL_REQUEST_DIAL:
1964             case RIL_REQUEST_ANSWER:
1965                 writeOnCallSolicitedResponse(phoneId, rilSerial, rilError, rilRequest);
1966                 break;
1967             case RIL_REQUEST_SEND_SMS:
1968             case RIL_REQUEST_SEND_SMS_EXPECT_MORE:
1969             case RIL_REQUEST_CDMA_SEND_SMS:
1970             case RIL_REQUEST_IMS_SEND_SMS:
1971                 SmsResponse smsResponse = (SmsResponse) ret;
1972                 writeOnSmsSolicitedResponse(phoneId, rilSerial, rilError, smsResponse);
1973                 break;
1974         }
1975     }
1976 
1977     /**
1978      * Write network validation event.
1979      * @param networkValidationState the network validation state.
1980      */
writeNetworkValidate(int networkValidationState)1981     public void writeNetworkValidate(int networkValidationState) {
1982         addTelephonyEvent(
1983                 new TelephonyEventBuilder().setNetworkValidate(networkValidationState).build());
1984     }
1985 
1986     /**
1987      * Write data switch event.
1988      * @param subId data switch to the subscription with this id.
1989      * @param dataSwitch the reason and state of data switch.
1990      */
writeDataSwitch(int subId, DataSwitch dataSwitch)1991     public void writeDataSwitch(int subId, DataSwitch dataSwitch) {
1992         int phoneId = SubscriptionManager.getPhoneId(subId);
1993         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDataSwitch(dataSwitch).build());
1994     }
1995 
1996     /**
1997      * Write on demand data switch event.
1998      * @param onDemandDataSwitch the apn and state of on demand data switch.
1999      */
writeOnDemandDataSwitch(OnDemandDataSwitch onDemandDataSwitch)2000     public void writeOnDemandDataSwitch(OnDemandDataSwitch onDemandDataSwitch) {
2001         addTelephonyEvent(
2002                 new TelephonyEventBuilder().setOnDemandDataSwitch(onDemandDataSwitch).build());
2003     }
2004 
2005     /**
2006      * Write phone state changed event
2007      *
2008      * @param phoneId Phone id
2009      * @param phoneState Phone state. See PhoneConstants.State for the details.
2010      */
writePhoneState(int phoneId, PhoneConstants.State phoneState)2011     public void writePhoneState(int phoneId, PhoneConstants.State phoneState) {
2012         int state;
2013         switch (phoneState) {
2014             case IDLE:
2015                 state = TelephonyCallSession.Event.PhoneState.STATE_IDLE;
2016                 break;
2017             case RINGING:
2018                 state = TelephonyCallSession.Event.PhoneState.STATE_RINGING;
2019                 break;
2020             case OFFHOOK:
2021                 state = TelephonyCallSession.Event.PhoneState.STATE_OFFHOOK;
2022                 break;
2023             default:
2024                 state = TelephonyCallSession.Event.PhoneState.STATE_UNKNOWN;
2025                 break;
2026         }
2027 
2028         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
2029         if (callSession == null) {
2030             Rlog.e(TAG, "writePhoneState: Call session is missing");
2031         } else {
2032             // For CS Calls Finish the Call Session after Receiving the Last Call Fail Cause
2033             // For IMS calls we receive the Disconnect Cause along with Call End event.
2034             // So we can finish the call session here.
2035             callSession.setLastKnownPhoneState(state);
2036             if ((state == TelephonyCallSession.Event.PhoneState.STATE_IDLE)
2037                     && (!callSession.containsCsCalls())) {
2038                 finishCallSession(callSession);
2039             }
2040             callSession.addEvent(new CallSessionEventBuilder(
2041                     TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED)
2042                     .setPhoneState(state));
2043         }
2044     }
2045 
2046     /**
2047      * Extracts the call ID from an ImsSession.
2048      *
2049      * @param session The session.
2050      * @return The call ID for the session, or -1 if none was found.
2051      */
getCallId(ImsCallSession session)2052     private int getCallId(ImsCallSession session) {
2053         if (session == null) {
2054             return -1;
2055         }
2056 
2057         try {
2058             return Integer.parseInt(session.getCallId());
2059         } catch (NumberFormatException nfe) {
2060             return -1;
2061         }
2062     }
2063 
2064     /**
2065      * Write IMS call state changed event
2066      *
2067      * @param phoneId Phone id
2068      * @param session IMS call session
2069      * @param callState IMS call state
2070      */
writeImsCallState(int phoneId, ImsCallSession session, ImsPhoneCall.State callState)2071     public void writeImsCallState(int phoneId, ImsCallSession session,
2072                                   ImsPhoneCall.State callState) {
2073         int state;
2074         switch (callState) {
2075             case IDLE:
2076                 state = TelephonyCallSession.Event.CallState.CALL_IDLE; break;
2077             case ACTIVE:
2078                 state = TelephonyCallSession.Event.CallState.CALL_ACTIVE; break;
2079             case HOLDING:
2080                 state = TelephonyCallSession.Event.CallState.CALL_HOLDING; break;
2081             case DIALING:
2082                 state = TelephonyCallSession.Event.CallState.CALL_DIALING; break;
2083             case ALERTING:
2084                 state = TelephonyCallSession.Event.CallState.CALL_ALERTING; break;
2085             case INCOMING:
2086                 state = TelephonyCallSession.Event.CallState.CALL_INCOMING; break;
2087             case WAITING:
2088                 state = TelephonyCallSession.Event.CallState.CALL_WAITING; break;
2089             case DISCONNECTED:
2090                 state = TelephonyCallSession.Event.CallState.CALL_DISCONNECTED; break;
2091             case DISCONNECTING:
2092                 state = TelephonyCallSession.Event.CallState.CALL_DISCONNECTING; break;
2093             default:
2094                 state = TelephonyCallSession.Event.CallState.CALL_UNKNOWN; break;
2095         }
2096 
2097         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
2098         if (callSession == null) {
2099             Rlog.e(TAG, "Call session is missing");
2100         } else {
2101             callSession.addEvent(new CallSessionEventBuilder(
2102                     TelephonyCallSession.Event.Type.IMS_CALL_STATE_CHANGED)
2103                     .setCallIndex(getCallId(session))
2104                     .setCallState(state));
2105         }
2106     }
2107 
2108     /**
2109      * Write IMS call start event
2110      *
2111      * @param phoneId Phone id
2112      * @param session IMS call session
2113      */
writeOnImsCallStart(int phoneId, ImsCallSession session)2114     public void writeOnImsCallStart(int phoneId, ImsCallSession session) {
2115         InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
2116 
2117         callSession.addEvent(
2118                 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_COMMAND)
2119                         .setCallIndex(getCallId(session))
2120                         .setImsCommand(TelephonyCallSession.Event.ImsCommand.IMS_CMD_START));
2121     }
2122 
2123     /**
2124      * Write IMS incoming call event
2125      *
2126      * @param phoneId Phone id
2127      * @param session IMS call session
2128      */
writeOnImsCallReceive(int phoneId, ImsCallSession session)2129     public void writeOnImsCallReceive(int phoneId, ImsCallSession session) {
2130         InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
2131 
2132         callSession.addEvent(
2133                 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_CALL_RECEIVE)
2134                         .setCallIndex(getCallId(session)));
2135     }
2136 
2137     /**
2138      * Write IMS command event
2139      *
2140      * @param phoneId Phone id
2141      * @param session IMS call session
2142      * @param command IMS command
2143      */
writeOnImsCommand(int phoneId, ImsCallSession session, int command)2144     public void writeOnImsCommand(int phoneId, ImsCallSession session, int command) {
2145 
2146         InProgressCallSession callSession =  mInProgressCallSessions.get(phoneId);
2147         if (callSession == null) {
2148             Rlog.e(TAG, "Call session is missing");
2149         } else {
2150             callSession.addEvent(
2151                     new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_COMMAND)
2152                             .setCallIndex(getCallId(session))
2153                             .setImsCommand(command));
2154         }
2155     }
2156 
2157     /**
2158      * Convert IMS reason info into proto
2159      *
2160      * @param reasonInfo IMS reason info
2161      * @return Converted proto
2162      */
toImsReasonInfoProto(ImsReasonInfo reasonInfo)2163     private TelephonyProto.ImsReasonInfo toImsReasonInfoProto(ImsReasonInfo reasonInfo) {
2164         TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo();
2165         if (reasonInfo != null) {
2166             ri.reasonCode = reasonInfo.getCode();
2167             ri.extraCode = reasonInfo.getExtraCode();
2168             String extraMessage = reasonInfo.getExtraMessage();
2169             if (extraMessage != null) {
2170                 ri.extraMessage = extraMessage;
2171             }
2172         }
2173         return ri;
2174     }
2175 
2176     /**
2177      * Convert CallQuality to proto.
2178      *
2179      * @param callQuality call quality to convert
2180      * @return Coverted proto
2181      */
toCallQualityProto( CallQuality callQuality)2182     public static TelephonyCallSession.Event.CallQuality toCallQualityProto(
2183             CallQuality callQuality) {
2184         TelephonyCallSession.Event.CallQuality cq = new TelephonyCallSession.Event.CallQuality();
2185         if (callQuality != null) {
2186             cq.downlinkLevel = callQualityLevelToProtoEnum(callQuality
2187                     .getDownlinkCallQualityLevel());
2188             cq.uplinkLevel = callQualityLevelToProtoEnum(callQuality.getUplinkCallQualityLevel());
2189             // callDuration is reported in millis, so convert to seconds
2190             cq.durationInSeconds = callQuality.getCallDuration() / 1000;
2191             cq.rtpPacketsTransmitted = callQuality.getNumRtpPacketsTransmitted();
2192             cq.rtpPacketsReceived = callQuality.getNumRtpPacketsReceived();
2193             cq.rtpPacketsTransmittedLost = callQuality.getNumRtpPacketsTransmittedLost();
2194             cq.rtpPacketsNotReceived = callQuality.getNumRtpPacketsNotReceived();
2195             cq.averageRelativeJitterMillis = callQuality.getAverageRelativeJitter();
2196             cq.maxRelativeJitterMillis = callQuality.getMaxRelativeJitter();
2197             cq.codecType = convertImsCodec(callQuality.getCodecType());
2198             cq.rtpInactivityDetected = callQuality.isRtpInactivityDetected();
2199             cq.rxSilenceDetected = callQuality.isIncomingSilenceDetectedAtCallSetup();
2200             cq.txSilenceDetected = callQuality.isOutgoingSilenceDetectedAtCallSetup();
2201         }
2202         return cq;
2203     }
2204 
2205     /**
2206      * Convert Call quality level into proto defined value.
2207      */
callQualityLevelToProtoEnum(int level)2208     private static int callQualityLevelToProtoEnum(int level) {
2209         if (level == CallQuality.CALL_QUALITY_EXCELLENT) {
2210             return TelephonyCallSession.Event.CallQuality.CallQualityLevel.EXCELLENT;
2211         } else if (level == CallQuality.CALL_QUALITY_GOOD) {
2212             return TelephonyCallSession.Event.CallQuality.CallQualityLevel.GOOD;
2213         } else if (level == CallQuality.CALL_QUALITY_FAIR) {
2214             return TelephonyCallSession.Event.CallQuality.CallQualityLevel.FAIR;
2215         } else if (level == CallQuality.CALL_QUALITY_POOR) {
2216             return TelephonyCallSession.Event.CallQuality.CallQualityLevel.POOR;
2217         } else if (level == CallQuality.CALL_QUALITY_BAD) {
2218             return TelephonyCallSession.Event.CallQuality.CallQualityLevel.BAD;
2219         } else if (level == CallQuality.CALL_QUALITY_NOT_AVAILABLE) {
2220             return TelephonyCallSession.Event.CallQuality.CallQualityLevel.NOT_AVAILABLE;
2221         } else {
2222             return TelephonyCallSession.Event.CallQuality.CallQualityLevel.UNDEFINED;
2223         }
2224     }
2225 
2226     /**
2227      * Write IMS call end event
2228      *
2229      * @param phoneId Phone id
2230      * @param session IMS call session
2231      * @param reasonInfo Call end reason
2232      * @param cqm Call Quality Metrics
2233      * @param emergencyNumber Emergency Number Info
2234      * @param countryIso Network country iso
2235      * @param emergencyNumberDatabaseVersion Emergency Number Database Version
2236      */
writeOnImsCallTerminated(int phoneId, ImsCallSession session, ImsReasonInfo reasonInfo, CallQualityMetrics cqm, EmergencyNumber emergencyNumber, String countryIso, int emergencyNumberDatabaseVersion)2237     public void writeOnImsCallTerminated(int phoneId, ImsCallSession session,
2238                                          ImsReasonInfo reasonInfo, CallQualityMetrics cqm,
2239                                          EmergencyNumber emergencyNumber, String countryIso,
2240                                          int emergencyNumberDatabaseVersion) {
2241         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
2242         if (callSession == null) {
2243             Rlog.e(TAG, "Call session is missing");
2244         } else {
2245             CallSessionEventBuilder callSessionEvent = new CallSessionEventBuilder(
2246                     TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED);
2247             callSessionEvent.setCallIndex(getCallId(session));
2248             callSessionEvent.setImsReasonInfo(toImsReasonInfoProto(reasonInfo));
2249 
2250             if (cqm != null) {
2251                 callSessionEvent.setCallQualitySummaryDl(cqm.getCallQualitySummaryDl())
2252                         .setCallQualitySummaryUl(cqm.getCallQualitySummaryUl());
2253             }
2254 
2255             if (emergencyNumber != null) {
2256                 /** Only collect this emergency number information per sample percentage */
2257                 if (ThreadLocalRandom.current().nextDouble(0, 100)
2258                         < getSamplePercentageForEmergencyCall(countryIso)) {
2259                     callSessionEvent.setIsImsEmergencyCall(true);
2260                     callSessionEvent.setImsEmergencyNumberInfo(
2261                             convertEmergencyNumberToEmergencyNumberInfo(emergencyNumber));
2262                     callSessionEvent.setEmergencyNumberDatabaseVersion(
2263                             emergencyNumberDatabaseVersion);
2264                 }
2265             }
2266             callSession.addEvent(callSessionEvent);
2267         }
2268     }
2269 
2270     /**
2271      * Write IMS call hangover event
2272      *
2273      * @param phoneId Phone id
2274      * @param eventType hangover type
2275      * @param session IMS call session
2276      * @param srcAccessTech Hangover starting RAT
2277      * @param targetAccessTech Hangover destination RAT
2278      * @param reasonInfo Hangover reason
2279      */
writeOnImsCallHandoverEvent(int phoneId, int eventType, ImsCallSession session, int srcAccessTech, int targetAccessTech, ImsReasonInfo reasonInfo)2280     public void writeOnImsCallHandoverEvent(int phoneId, int eventType, ImsCallSession session,
2281                                             int srcAccessTech, int targetAccessTech,
2282                                             ImsReasonInfo reasonInfo) {
2283         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
2284         if (callSession == null) {
2285             Rlog.e(TAG, "Call session is missing");
2286         } else {
2287             callSession.addEvent(
2288                     new CallSessionEventBuilder(eventType)
2289                             .setCallIndex(getCallId(session))
2290                             .setSrcAccessTech(srcAccessTech)
2291                             .setTargetAccessTech(targetAccessTech)
2292                             .setImsReasonInfo(toImsReasonInfoProto(reasonInfo)));
2293         }
2294     }
2295 
2296     /**
2297      * Write Send SMS event
2298      *
2299      * @param phoneId Phone id
2300      * @param rilSerial RIL request serial number
2301      * @param tech SMS RAT
2302      * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
2303      *         {@link SmsMessage#FORMAT_3GPP2}.
2304      * @param messageId Unique id for this message.
2305      */
writeRilSendSms(int phoneId, int rilSerial, int tech, int format, long messageId)2306     public synchronized void writeRilSendSms(int phoneId, int rilSerial, int tech, int format,
2307             long messageId) {
2308         InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
2309 
2310         smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND)
2311                 .setTech(tech)
2312                 .setRilRequestId(rilSerial)
2313                 .setFormat(format)
2314                 .setMessageId(messageId)
2315         );
2316 
2317         smsSession.increaseExpectedResponse();
2318     }
2319 
2320     /**
2321      * Write Send SMS event using ImsService. Expecting response from
2322      * {@link #writeOnSmsSolicitedResponse}.
2323      *
2324      * @param phoneId Phone id
2325      * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
2326      *         {@link SmsMessage#FORMAT_3GPP2}.
2327      * @param resultCode The result of sending the new SMS to the vendor layer to be sent to the
2328      *         carrier network.
2329      * @param messageId Unique id for this message.
2330      */
writeImsServiceSendSms(int phoneId, String format, @ImsSmsImplBase.SendStatusResult int resultCode, long messageId)2331     public synchronized void writeImsServiceSendSms(int phoneId, String format,
2332             @ImsSmsImplBase.SendStatusResult int resultCode, long messageId) {
2333         InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
2334         smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND)
2335                 .setTech(SmsSession.Event.Tech.SMS_IMS)
2336                 .setImsServiceErrno(resultCode)
2337                 .setFormat(convertSmsFormat(format))
2338                 .setMessageId(messageId)
2339         );
2340 
2341         smsSession.increaseExpectedResponse();
2342     }
2343 
2344     /**
2345      * Write incoming Broadcast SMS event
2346      *
2347      * @param phoneId Phone id
2348      * @param format CB msg format
2349      * @param priority CB msg priority
2350      * @param isCMAS true if msg is CMAS
2351      * @param isETWS true if msg is ETWS
2352      * @param serviceCategory Service category of CB msg
2353      * @param serialNumber Serial number of the message
2354      * @param deliveredTimestamp Message's delivered timestamp
2355      */
writeNewCBSms(int phoneId, int format, int priority, boolean isCMAS, boolean isETWS, int serviceCategory, int serialNumber, long deliveredTimestamp)2356     public synchronized void writeNewCBSms(int phoneId, int format, int priority, boolean isCMAS,
2357                                            boolean isETWS, int serviceCategory, int serialNumber,
2358                                            long deliveredTimestamp) {
2359         InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
2360 
2361         int type;
2362         if (isCMAS) {
2363             type = SmsSession.Event.CBMessageType.CMAS;
2364         } else if (isETWS) {
2365             type = SmsSession.Event.CBMessageType.ETWS;
2366         } else {
2367             type = SmsSession.Event.CBMessageType.OTHER;
2368         }
2369 
2370         SmsSession.Event.CBMessage cbm = new SmsSession.Event.CBMessage();
2371         cbm.msgFormat = format;
2372         cbm.msgPriority = priority + 1;
2373         cbm.msgType = type;
2374         cbm.serviceCategory = serviceCategory;
2375         cbm.serialNumber = serialNumber;
2376         cbm.deliveredTimestampMillis = deliveredTimestamp;
2377 
2378         smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.CB_SMS_RECEIVED)
2379                 .setCellBroadcastMessage(cbm)
2380         );
2381 
2382         finishSmsSessionIfNeeded(smsSession);
2383     }
2384 
2385     /**
2386      * Write an incoming multi-part SMS that was discarded because some parts were missing
2387      *
2388      * @param phoneId Phone id
2389      * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
2390      *         {@link SmsMessage#FORMAT_3GPP2}.
2391      * @param receivedCount Number of received parts.
2392      * @param totalCount Total number of parts of the SMS.
2393      */
writeDroppedIncomingMultipartSms(int phoneId, String format, int receivedCount, int totalCount)2394     public void writeDroppedIncomingMultipartSms(int phoneId, String format,
2395             int receivedCount, int totalCount) {
2396         logv("Logged dropped multipart SMS: received " + receivedCount
2397                 + " out of " + totalCount);
2398 
2399         SmsSession.Event.IncompleteSms details = new SmsSession.Event.IncompleteSms();
2400         details.receivedParts = receivedCount;
2401         details.totalParts = totalCount;
2402 
2403         InProgressSmsSession smsSession = startNewSmsSession(phoneId);
2404         smsSession.addEvent(
2405                 new SmsSessionEventBuilder(SmsSession.Event.Type.INCOMPLETE_SMS_RECEIVED)
2406                     .setFormat(convertSmsFormat(format))
2407                     .setIncompleteSms(details));
2408 
2409         finishSmsSession(smsSession);
2410     }
2411 
2412     /**
2413      * Write a generic SMS of any type
2414      *
2415      * @param phoneId Phone id
2416      * @param type Type of the SMS.
2417      * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
2418      *         {@link SmsMessage#FORMAT_3GPP2}.
2419      * @param success Indicates if the SMS-PP was successfully delivered to the USIM.
2420      */
writeIncomingSmsWithType(int phoneId, int type, String format, boolean success)2421     private void writeIncomingSmsWithType(int phoneId, int type, String format, boolean success) {
2422         InProgressSmsSession smsSession = startNewSmsSession(phoneId);
2423         smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
2424                 .setFormat(convertSmsFormat(format))
2425                 .setSmsType(type)
2426                 .setErrorCode(success ? SmsManager.RESULT_ERROR_NONE :
2427                     SmsManager.RESULT_ERROR_GENERIC_FAILURE));
2428         finishSmsSession(smsSession);
2429     }
2430 
2431     /**
2432      * Write an incoming SMS-PP for the USIM
2433      *
2434      * @param phoneId Phone id
2435      * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
2436      *         {@link SmsMessage#FORMAT_3GPP2}.
2437      * @param success Indicates if the SMS-PP was successfully delivered to the USIM.
2438      */
writeIncomingSMSPP(int phoneId, String format, boolean success)2439     public void writeIncomingSMSPP(int phoneId, String format, boolean success) {
2440         logv("Logged SMS-PP session. Result = " + success);
2441         writeIncomingSmsWithType(phoneId,
2442                 SmsSession.Event.SmsType.SMS_TYPE_SMS_PP, format, success);
2443     }
2444 
2445     /**
2446      * Write an incoming SMS to update voicemail indicator
2447      *
2448      * @param phoneId Phone id
2449      * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
2450      *         {@link SmsMessage#FORMAT_3GPP2}.
2451      */
writeIncomingVoiceMailSms(int phoneId, String format)2452     public void writeIncomingVoiceMailSms(int phoneId, String format) {
2453         logv("Logged VoiceMail message.");
2454         writeIncomingSmsWithType(phoneId,
2455                 SmsSession.Event.SmsType.SMS_TYPE_VOICEMAIL_INDICATION, format, true);
2456     }
2457 
2458     /**
2459      * Write an incoming SMS of type 0
2460      *
2461      * @param phoneId Phone id
2462      * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
2463      *         {@link SmsMessage#FORMAT_3GPP2}.
2464      */
writeIncomingSmsTypeZero(int phoneId, String format)2465     public void writeIncomingSmsTypeZero(int phoneId, String format) {
2466         logv("Logged Type-0 SMS message.");
2467         writeIncomingSmsWithType(phoneId,
2468                 SmsSession.Event.SmsType.SMS_TYPE_ZERO, format, true);
2469     }
2470 
2471     /**
2472      * Write a successful incoming SMS session
2473      *
2474      * @param phoneId Phone id
2475      * @param type Type of the SMS.
2476      * @param smsSource the source of the SMS message
2477      * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
2478      *         {@link SmsMessage#FORMAT_3GPP2}.
2479      * @param timestamps array with timestamps of each incoming SMS part. It contains a single
2480      * @param blocked indicates if the message was blocked or not.
2481      * @param success Indicates if the SMS-PP was successfully delivered to the USIM.
2482      * @param messageId Unique id for this message.
2483      */
writeIncomingSmsSessionWithType(int phoneId, int type, @InboundSmsHandler.SmsSource int smsSource, String format, long[] timestamps, boolean blocked, boolean success, long messageId)2484     private void writeIncomingSmsSessionWithType(int phoneId, int type,
2485             @InboundSmsHandler.SmsSource int smsSource, String format, long[] timestamps,
2486             boolean blocked, boolean success, long messageId) {
2487         logv("Logged SMS session consisting of " + timestamps.length
2488                 + " parts, source = " + smsSource
2489                 + " blocked = " + blocked
2490                 + " type = " + type
2491                 + " " + SmsController.formatCrossStackMessageId(messageId));
2492 
2493         int smsFormat = convertSmsFormat(format);
2494         int smsError =
2495                 success ? SmsManager.RESULT_ERROR_NONE : SmsManager.RESULT_ERROR_GENERIC_FAILURE;
2496         int smsTech = getSmsTech(smsSource, smsFormat == SmsSession.Event.Format.SMS_FORMAT_3GPP2);
2497 
2498         InProgressSmsSession smsSession = startNewSmsSession(phoneId);
2499 
2500         long startElapsedTimeMillis = SystemClock.elapsedRealtime();
2501         for (int i = 0; i < timestamps.length; i++) {
2502             SmsSessionEventBuilder eventBuilder =
2503                     new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
2504                         .setFormat(smsFormat)
2505                         .setTech(smsTech)
2506                         .setErrorCode(smsError)
2507                         .setSmsType(type)
2508                         .setBlocked(blocked)
2509                         .setMessageId(messageId);
2510             long interval = (i > 0) ? timestamps[i] - timestamps[i - 1] : 0;
2511             smsSession.addEvent(startElapsedTimeMillis + interval, eventBuilder);
2512         }
2513         finishSmsSession(smsSession);
2514     }
2515 
2516     /**
2517      * Write an incoming WAP-PUSH message.
2518      *
2519      * @param phoneId Phone id
2520      * @param smsSource the source of the SMS message
2521      * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
2522      *         {@link SmsMessage#FORMAT_3GPP2}.
2523      * @param timestamps array with timestamps of each incoming SMS part. It contains a single
2524      * @param success Indicates if the SMS-PP was successfully delivered to the USIM.
2525      * @param messageId Unique id for this message.
2526      */
writeIncomingWapPush(int phoneId, @InboundSmsHandler.SmsSource int smsSource, String format, long[] timestamps, boolean success, long messageId)2527     public void writeIncomingWapPush(int phoneId, @InboundSmsHandler.SmsSource int smsSource,
2528             String format, long[] timestamps, boolean success, long messageId) {
2529         writeIncomingSmsSessionWithType(phoneId, SmsSession.Event.SmsType.SMS_TYPE_WAP_PUSH,
2530                 smsSource, format, timestamps, false, success, messageId);
2531     }
2532 
2533     /**
2534      * Write a successful incoming SMS session
2535      *
2536      * @param phoneId Phone id
2537      * @param smsSource the source of the SMS message
2538      * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or
2539      *         {@link SmsMessage#FORMAT_3GPP2}.
2540      * @param timestamps array with timestamps of each incoming SMS part. It contains a single
2541      * @param blocked indicates if the message was blocked or not.
2542      * @param messageId Unique id for this message.
2543      */
writeIncomingSmsSession(int phoneId, @InboundSmsHandler.SmsSource int smsSource, String format, long[] timestamps, boolean blocked, long messageId)2544     public void writeIncomingSmsSession(int phoneId, @InboundSmsHandler.SmsSource int smsSource,
2545             String format, long[] timestamps, boolean blocked, long messageId) {
2546         writeIncomingSmsSessionWithType(phoneId, SmsSession.Event.SmsType.SMS_TYPE_NORMAL,
2547                 smsSource, format, timestamps, blocked, true, messageId);
2548     }
2549 
2550     /**
2551      * Write an error incoming SMS
2552      *
2553      * @param phoneId Phone id
2554      * @param is3gpp2 true for 3GPP2 format, false for 3GPP format.
2555      * @param smsSource the source of the SMS message
2556      * @param result Indicates the reason of the failure.
2557      */
writeIncomingSmsError(int phoneId, boolean is3gpp2, @InboundSmsHandler.SmsSource int smsSource, int result)2558     public void writeIncomingSmsError(int phoneId, boolean is3gpp2,
2559             @InboundSmsHandler.SmsSource int smsSource, int result) {
2560         logv("Incoming SMS error = " + result);
2561 
2562         int smsError = SmsManager.RESULT_ERROR_GENERIC_FAILURE;
2563         switch (result) {
2564             case Intents.RESULT_SMS_HANDLED:
2565                 // This should not happen.
2566                 return;
2567             case Intents.RESULT_SMS_OUT_OF_MEMORY:
2568                 smsError = SmsManager.RESULT_NO_MEMORY;
2569                 break;
2570             case Intents.RESULT_SMS_UNSUPPORTED:
2571                 smsError = SmsManager.RESULT_REQUEST_NOT_SUPPORTED;
2572                 break;
2573             case Intents.RESULT_SMS_GENERIC_ERROR:
2574             default:
2575                 smsError = SmsManager.RESULT_ERROR_GENERIC_FAILURE;
2576                 break;
2577         }
2578 
2579         InProgressSmsSession smsSession = startNewSmsSession(phoneId);
2580 
2581         SmsSessionEventBuilder eventBuilder =
2582                 new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
2583                     .setFormat(is3gpp2
2584                                 ? SmsSession.Event.Format.SMS_FORMAT_3GPP2
2585                                 : SmsSession.Event.Format.SMS_FORMAT_3GPP)
2586                     .setTech(getSmsTech(smsSource, is3gpp2))
2587                     .setErrorCode(smsError);
2588         smsSession.addEvent(eventBuilder);
2589         finishSmsSession(smsSession);
2590     }
2591 
2592     /**
2593      * Write NITZ event
2594      *
2595      * @param phoneId Phone id
2596      * @param timestamp NITZ time in milliseconds
2597      */
writeNITZEvent(int phoneId, long timestamp)2598     public void writeNITZEvent(int phoneId, long timestamp) {
2599         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setNITZ(timestamp).build();
2600         addTelephonyEvent(event);
2601 
2602         annotateInProgressCallSession(event.timestampMillis, phoneId,
2603                 new CallSessionEventBuilder(
2604                         TelephonyCallSession.Event.Type.NITZ_TIME)
2605                         .setNITZ(timestamp));
2606     }
2607 
2608     /**
2609      * Write Modem Restart event
2610      *
2611      * @param phoneId Phone id
2612      * @param reason Reason for the modem reset.
2613      */
writeModemRestartEvent(int phoneId, String reason)2614     public void writeModemRestartEvent(int phoneId, String reason) {
2615         final ModemRestart modemRestart = new ModemRestart();
2616         String basebandVersion = Build.getRadioVersion();
2617         if (basebandVersion != null) modemRestart.basebandVersion = basebandVersion;
2618         if (reason != null) modemRestart.reason = reason;
2619         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setModemRestart(
2620                 modemRestart).build();
2621         addTelephonyEvent(event);
2622     }
2623 
2624     /**
2625      * Write carrier identification matching event
2626      *
2627      * @param phoneId Phone id
2628      * @param version Carrier table version
2629      * @param cid Unique Carrier Id
2630      * @param unknownMcmnc MCC and MNC that map to this carrier
2631      * @param unknownGid1 Group id level 1
2632      * @param simInfo Subscription info
2633      */
writeCarrierIdMatchingEvent(int phoneId, int version, int cid, String unknownMcmnc, String unknownGid1, CarrierResolver.CarrierMatchingRule simInfo)2634     public void writeCarrierIdMatchingEvent(int phoneId, int version, int cid,
2635                                             String unknownMcmnc, String unknownGid1,
2636                                             CarrierResolver.CarrierMatchingRule simInfo) {
2637         final CarrierIdMatching carrierIdMatching = new CarrierIdMatching();
2638         final CarrierIdMatchingResult carrierIdMatchingResult = new CarrierIdMatchingResult();
2639 
2640         // fill in information for unknown mccmnc and gid1 for unidentified carriers.
2641         if (cid != TelephonyManager.UNKNOWN_CARRIER_ID) {
2642             // Successful matching event if result only has carrierId
2643             carrierIdMatchingResult.carrierId = cid;
2644             // Unknown Gid1 event if result only has carrierId, gid1 and mccmnc
2645             if (unknownGid1 != null) {
2646                 carrierIdMatchingResult.unknownMccmnc = unknownMcmnc;
2647                 carrierIdMatchingResult.unknownGid1 = unknownGid1;
2648             }
2649         } else {
2650             // Unknown mccmnc event if result only has mccmnc
2651             if (unknownMcmnc != null) {
2652                 carrierIdMatchingResult.unknownMccmnc = unknownMcmnc;
2653             }
2654         }
2655 
2656         // fill in complete matching information from the SIM.
2657         carrierIdMatchingResult.mccmnc = TelephonyUtils.emptyIfNull(simInfo.mccMnc);
2658         carrierIdMatchingResult.spn = TelephonyUtils.emptyIfNull(simInfo.spn);
2659         carrierIdMatchingResult.pnn = TelephonyUtils.emptyIfNull(simInfo.plmn);
2660         carrierIdMatchingResult.gid1 = TelephonyUtils.emptyIfNull(simInfo.gid1);
2661         carrierIdMatchingResult.gid2 = TelephonyUtils.emptyIfNull(simInfo.gid2);
2662         carrierIdMatchingResult.imsiPrefix = TelephonyUtils.emptyIfNull(simInfo.imsiPrefixPattern);
2663         carrierIdMatchingResult.iccidPrefix = TelephonyUtils.emptyIfNull(simInfo.iccidPrefix);
2664         carrierIdMatchingResult.preferApn = TelephonyUtils.emptyIfNull(simInfo.apn);
2665         if (simInfo.privilegeAccessRule != null) {
2666             carrierIdMatchingResult.privilegeAccessRule =
2667                     simInfo.privilegeAccessRule.stream().toArray(String[]::new);
2668         }
2669 
2670         carrierIdMatching.cidTableVersion = version;
2671         carrierIdMatching.result = carrierIdMatchingResult;
2672 
2673         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setCarrierIdMatching(
2674                 carrierIdMatching).build();
2675         mLastCarrierId.put(phoneId, carrierIdMatching);
2676         addTelephonyEvent(event);
2677     }
2678 
2679     /**
2680      * Write emergency number update event
2681      *
2682      * @param emergencyNumber Updated emergency number
2683      */
writeEmergencyNumberUpdateEvent(int phoneId, EmergencyNumber emergencyNumber, int emergencyNumberDatabaseVersion)2684     public void writeEmergencyNumberUpdateEvent(int phoneId, EmergencyNumber emergencyNumber,
2685             int emergencyNumberDatabaseVersion) {
2686         if (emergencyNumber == null) {
2687             return;
2688         }
2689         final EmergencyNumberInfo emergencyNumberInfo =
2690                 convertEmergencyNumberToEmergencyNumberInfo(emergencyNumber);
2691 
2692         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setUpdatedEmergencyNumber(
2693                 emergencyNumberInfo, emergencyNumberDatabaseVersion).build();
2694         addTelephonyEvent(event);
2695     }
2696 
2697     /**
2698      * Write network capabilities changed event
2699      *
2700      * @param phoneId Phone id
2701      * @param networkCapabilities Network capabilities
2702      */
writeNetworkCapabilitiesChangedEvent(int phoneId, NetworkCapabilities networkCapabilities)2703     public void writeNetworkCapabilitiesChangedEvent(int phoneId,
2704             NetworkCapabilities networkCapabilities) {
2705         final NetworkCapabilitiesInfo caps = new NetworkCapabilitiesInfo();
2706         caps.isNetworkUnmetered = networkCapabilities.hasCapability(
2707                 NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
2708 
2709         TelephonyEvent event = new TelephonyEventBuilder(phoneId)
2710                 .setNetworkCapabilities(caps).build();
2711         mLastNetworkCapabilitiesInfos.put(phoneId, caps);
2712         addTelephonyEvent(event);
2713     }
2714 
2715     /** Write radio state changed event */
writeRadioState(int phoneId, @RadioPowerState int state)2716     public void writeRadioState(int phoneId, @RadioPowerState int state) {
2717         int radioState = convertRadioState(state);
2718         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setRadioState(radioState).build();
2719         mLastRadioState.put(phoneId, radioState);
2720         addTelephonyEvent(event);
2721     }
2722 
convertRadioState(@adioPowerState int state)2723     private static int convertRadioState(@RadioPowerState int state) {
2724         switch (state) {
2725             case TelephonyManager.RADIO_POWER_OFF:
2726                 return RadioState.RADIO_STATE_OFF;
2727             case TelephonyManager.RADIO_POWER_ON:
2728                 return RadioState.RADIO_STATE_ON;
2729             case TelephonyManager.RADIO_POWER_UNAVAILABLE:
2730                 return RadioState.RADIO_STATE_UNAVAILABLE;
2731             default:
2732                 return RadioState.RADIO_STATE_UNKNOWN;
2733         }
2734     }
2735 
2736     /**
2737      * Convert SMS format
2738      */
convertSmsFormat(String format)2739     private int convertSmsFormat(String format) {
2740         int formatCode = SmsSession.Event.Format.SMS_FORMAT_UNKNOWN;
2741         switch (format) {
2742             case SmsMessage.FORMAT_3GPP : {
2743                 formatCode = SmsSession.Event.Format.SMS_FORMAT_3GPP;
2744                 break;
2745             }
2746             case SmsMessage.FORMAT_3GPP2: {
2747                 formatCode = SmsSession.Event.Format.SMS_FORMAT_3GPP2;
2748                 break;
2749             }
2750         }
2751         return formatCode;
2752     }
2753 
2754     /**
2755      * Get SMS technology
2756      */
getSmsTech(@nboundSmsHandler.SmsSource int smsSource, boolean is3gpp2)2757     private int getSmsTech(@InboundSmsHandler.SmsSource int smsSource, boolean is3gpp2) {
2758         if (smsSource == SOURCE_INJECTED_FROM_IMS) {
2759             return SmsSession.Event.Tech.SMS_IMS;
2760         } else if (smsSource == SOURCE_NOT_INJECTED) {
2761             return is3gpp2 ? SmsSession.Event.Tech.SMS_CDMA : SmsSession.Event.Tech.SMS_GSM;
2762         } else { // SOURCE_INJECTED_FROM_UNKNOWN
2763             return SmsSession.Event.Tech.SMS_UNKNOWN;
2764         }
2765     }
2766 
2767     /**
2768      * Convert IMS audio codec into proto defined value
2769      *
2770      * @param c IMS codec value
2771      * @return Codec value defined in call session proto
2772      */
convertImsCodec(int c)2773     private static int convertImsCodec(int c) {
2774         switch (c) {
2775             case ImsStreamMediaProfile.AUDIO_QUALITY_AMR:
2776                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR;
2777             case ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB:
2778                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR_WB;
2779             case ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K:
2780                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_QCELP13K;
2781             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC:
2782                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC;
2783             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B:
2784                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_B;
2785             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB:
2786                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_WB;
2787             case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW:
2788                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_NW;
2789             case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR:
2790                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_EFR;
2791             case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR:
2792                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_FR;
2793             case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR:
2794                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_HR;
2795             case ImsStreamMediaProfile.AUDIO_QUALITY_G711U:
2796                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711U;
2797             case ImsStreamMediaProfile.AUDIO_QUALITY_G723:
2798                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G723;
2799             case ImsStreamMediaProfile.AUDIO_QUALITY_G711A:
2800                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711A;
2801             case ImsStreamMediaProfile.AUDIO_QUALITY_G722:
2802                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G722;
2803             case ImsStreamMediaProfile.AUDIO_QUALITY_G711AB:
2804                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711AB;
2805             case ImsStreamMediaProfile.AUDIO_QUALITY_G729:
2806                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G729;
2807             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB:
2808                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_NB;
2809             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB:
2810                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_WB;
2811             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB:
2812                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_SWB;
2813             case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB:
2814                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_FB;
2815             default:
2816                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_UNKNOWN;
2817         }
2818     }
2819 
2820     /**
2821      * Convert GSM/CDMA audio codec into proto defined value
2822      *
2823      * @param c GSM/CDMA codec value
2824      * @return Codec value defined in call session proto
2825      */
convertGsmCdmaCodec(int c)2826     private int convertGsmCdmaCodec(int c) {
2827         switch (c) {
2828             case DriverCall.AUDIO_QUALITY_AMR:
2829                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR;
2830             case DriverCall.AUDIO_QUALITY_AMR_WB:
2831                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR_WB;
2832             case DriverCall.AUDIO_QUALITY_GSM_EFR:
2833                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_EFR;
2834             case DriverCall.AUDIO_QUALITY_GSM_FR:
2835                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_FR;
2836             case DriverCall.AUDIO_QUALITY_GSM_HR:
2837                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_HR;
2838             case DriverCall.AUDIO_QUALITY_EVRC:
2839                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC;
2840             case DriverCall.AUDIO_QUALITY_EVRC_B:
2841                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_B;
2842             case DriverCall.AUDIO_QUALITY_EVRC_WB:
2843                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_WB;
2844             case DriverCall.AUDIO_QUALITY_EVRC_NW:
2845                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_NW;
2846             default:
2847                 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_UNKNOWN;
2848         }
2849     }
2850 
2851     /**
2852      * Write audio codec event
2853      *
2854      * @param phoneId Phone id
2855      * @param session IMS call session
2856      */
writeAudioCodecIms(int phoneId, ImsCallSession session)2857     public void writeAudioCodecIms(int phoneId, ImsCallSession session) {
2858         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
2859         if (callSession == null) {
2860             Rlog.e(TAG, "Call session is missing");
2861             return;
2862         }
2863 
2864         ImsCallProfile localCallProfile = session.getLocalCallProfile();
2865         if (localCallProfile != null) {
2866             int codec = convertImsCodec(localCallProfile.mMediaProfile.mAudioQuality);
2867             callSession.addEvent(new CallSessionEventBuilder(
2868                     TelephonyCallSession.Event.Type.AUDIO_CODEC)
2869                     .setCallIndex(getCallId(session))
2870                     .setAudioCodec(codec));
2871 
2872             logv("Logged Audio Codec event. Value: " + codec);
2873         }
2874     }
2875 
2876     /**
2877      * Write audio codec event
2878      *
2879      * @param phoneId Phone id
2880      * @param audioQuality Audio quality value
2881      */
writeAudioCodecGsmCdma(int phoneId, int audioQuality)2882     public void writeAudioCodecGsmCdma(int phoneId, int audioQuality) {
2883         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
2884         if (callSession == null) {
2885             Rlog.e(TAG, "Call session is missing");
2886             return;
2887         }
2888 
2889         int codec = convertGsmCdmaCodec(audioQuality);
2890         callSession.addEvent(new CallSessionEventBuilder(
2891                 TelephonyCallSession.Event.Type.AUDIO_CODEC)
2892                 .setAudioCodec(codec));
2893 
2894         logv("Logged Audio Codec event. Value: " + codec);
2895     }
2896 
2897     //TODO: Expand the proto in the future
writeOnImsCallInitiating(int phoneId, ImsCallSession session)2898     public void writeOnImsCallInitiating(int phoneId, ImsCallSession session) {}
writeOnImsCallProgressing(int phoneId, ImsCallSession session)2899     public void writeOnImsCallProgressing(int phoneId, ImsCallSession session) {}
writeOnImsCallStarted(int phoneId, ImsCallSession session)2900     public void writeOnImsCallStarted(int phoneId, ImsCallSession session) {}
writeOnImsCallStartFailed(int phoneId, ImsCallSession session, ImsReasonInfo reasonInfo)2901     public void writeOnImsCallStartFailed(int phoneId, ImsCallSession session,
2902                                           ImsReasonInfo reasonInfo) {}
writeOnImsCallHeld(int phoneId, ImsCallSession session)2903     public void writeOnImsCallHeld(int phoneId, ImsCallSession session) {}
writeOnImsCallHoldReceived(int phoneId, ImsCallSession session)2904     public void writeOnImsCallHoldReceived(int phoneId, ImsCallSession session) {}
writeOnImsCallHoldFailed(int phoneId, ImsCallSession session, ImsReasonInfo reasonInfo)2905     public void writeOnImsCallHoldFailed(int phoneId, ImsCallSession session,
2906                                          ImsReasonInfo reasonInfo) {}
writeOnImsCallResumed(int phoneId, ImsCallSession session)2907     public void writeOnImsCallResumed(int phoneId, ImsCallSession session) {}
writeOnImsCallResumeReceived(int phoneId, ImsCallSession session)2908     public void writeOnImsCallResumeReceived(int phoneId, ImsCallSession session) {}
writeOnImsCallResumeFailed(int phoneId, ImsCallSession session, ImsReasonInfo reasonInfo)2909     public void writeOnImsCallResumeFailed(int phoneId, ImsCallSession session,
2910                                            ImsReasonInfo reasonInfo) {}
writeOnRilTimeoutResponse(int phoneId, int rilSerial, int rilRequest)2911     public void writeOnRilTimeoutResponse(int phoneId, int rilSerial, int rilRequest) {}
2912 
2913     /**
2914      * Get the sample percentage of collecting metrics based on countries' population.
2915      *
2916      * The larger population the country has, the lower percentage we use to collect this
2917      * metrics. Since the exact population changes frequently, buckets of the population are used
2918      * instead of its exact number. Seven different levels of sampling percentage are assigned
2919      * based on the scale of population for countries.
2920      */
getSamplePercentageForEmergencyCall(String countryIso)2921     private double getSamplePercentageForEmergencyCall(String countryIso) {
2922         String countriesFor1Percentage = "cn,in";
2923         String countriesFor5Percentage = "us,id,br,pk,ng,bd,ru,mx,jp,et,ph,eg,vn,cd,tr,ir,de";
2924         String countriesFor15Percentage = "th,gb,fr,tz,it,za,mm,ke,kr,co,es,ug,ar,ua,dz,sd,iq";
2925         String countriesFor25Percentage = "pl,ca,af,ma,sa,pe,uz,ve,my,ao,mz,gh,np,ye,mg,kp,cm";
2926         String countriesFor35Percentage = "au,tw,ne,lk,bf,mw,ml,ro,kz,sy,cl,zm,gt,zw,nl,ec,sn";
2927         String countriesFor45Percentage = "kh,td,so,gn,ss,rw,bj,tn,bi,be,cu,bo,ht,gr,do,cz,pt";
2928         if (countriesFor1Percentage.contains(countryIso)) {
2929             return 1;
2930         } else if (countriesFor5Percentage.contains(countryIso)) {
2931             return 5;
2932         } else if (countriesFor15Percentage.contains(countryIso)) {
2933             return 15;
2934         } else if (countriesFor25Percentage.contains(countryIso)) {
2935             return 25;
2936         } else if (countriesFor35Percentage.contains(countryIso)) {
2937             return 35;
2938         } else if (countriesFor45Percentage.contains(countryIso)) {
2939             return 45;
2940         } else {
2941             return 50;
2942         }
2943     }
2944 
mapSimStateToProto(int simState)2945     private static int mapSimStateToProto(int simState) {
2946         switch (simState) {
2947             case TelephonyManager.SIM_STATE_ABSENT:
2948                 return SimState.SIM_STATE_ABSENT;
2949             case TelephonyManager.SIM_STATE_LOADED:
2950                 return SimState.SIM_STATE_LOADED;
2951             default:
2952                 return SimState.SIM_STATE_UNKNOWN;
2953         }
2954     }
2955 
2956     /**
2957      * Write bandwidth estimator stats
2958      */
writeBandwidthStats(int link, int rat, int nrMode, int signalLevel, int bwEstExtErrPercent, int coldStartErrPercent, int bwKbps)2959     public synchronized void writeBandwidthStats(int link, int rat, int nrMode,
2960             int signalLevel, int bwEstExtErrPercent, int coldStartErrPercent, int bwKbps) {
2961         BwEstimationStats stats = lookupEstimationStats(link, rat, nrMode);
2962         stats.mBwEstErrorAcc[signalLevel] += Math.abs(bwEstExtErrPercent);
2963         stats.mStaticBwErrorAcc[signalLevel] += Math.abs(coldStartErrPercent);
2964         stats.mBwAccKbps[signalLevel] += bwKbps;
2965         stats.mCount[signalLevel]++;
2966     }
2967 
lookupEstimationStats(int linkIndex, int dataRat, int nrMode)2968     private BwEstimationStats lookupEstimationStats(int linkIndex, int dataRat, int nrMode) {
2969         String dataRatName = LinkBandwidthEstimator.getDataRatName(dataRat, nrMode);
2970         BwEstimationStats ans = mBwEstStatsMapList.get(linkIndex).get(dataRatName);
2971         if (ans == null) {
2972             ans = new BwEstimationStats(dataRat, nrMode);
2973             mBwEstStatsMapList.get(linkIndex).put(dataRatName, ans);
2974         }
2975         return ans;
2976     }
2977 
buildBandwidthEstimatorStats()2978     private BandwidthEstimatorStats buildBandwidthEstimatorStats() {
2979         BandwidthEstimatorStats stats = new BandwidthEstimatorStats();
2980         List<BandwidthEstimatorStats.PerRat> ratList;
2981         ratList = writeBandwidthEstimatorStatsRatList(mBwEstStatsMapList.get(0));
2982         stats.perRatTx = ratList.toArray(new BandwidthEstimatorStats.PerRat[0]);
2983         ratList = writeBandwidthEstimatorStatsRatList(mBwEstStatsMapList.get(1));
2984         stats.perRatRx = ratList.toArray(new BandwidthEstimatorStats.PerRat[0]);
2985         return stats;
2986     }
2987 
writeBandwidthEstimatorStatsRatList( Map<String, BwEstimationStats> bwEstStatsMap)2988     private List<BandwidthEstimatorStats.PerRat> writeBandwidthEstimatorStatsRatList(
2989             Map<String, BwEstimationStats> bwEstStatsMap) {
2990         List<BandwidthEstimatorStats.PerRat> ratList = new ArrayList<>();
2991         for (BwEstimationStats perRat : bwEstStatsMap.values()) {
2992             ratList.add(perRat.writeBandwidthStats());
2993         }
2994         return ratList;
2995     }
2996 
2997     private static class BwEstimationStats {
2998         final int mRadioTechnology;
2999         final int mNrMode;
3000         final long[] mBwEstErrorAcc = new long[NUM_SIGNAL_LEVEL];
3001         final long[] mStaticBwErrorAcc = new long[NUM_SIGNAL_LEVEL];
3002         final long[] mBwAccKbps = new long[NUM_SIGNAL_LEVEL];
3003         final int[] mCount = new int[NUM_SIGNAL_LEVEL];
3004 
BwEstimationStats(int radioTechnology, int nrMode)3005         BwEstimationStats(int radioTechnology, int nrMode) {
3006             mRadioTechnology = radioTechnology;
3007             mNrMode = nrMode;
3008         }
3009 
3010         @Override
toString()3011         public String toString() {
3012             StringBuilder sb = new StringBuilder();
3013             return sb.append(LinkBandwidthEstimator.getDataRatName(mRadioTechnology, mNrMode))
3014                     .append("\n Count\n").append(printValues(mCount))
3015                     .append("\n AvgKbps\n").append(printAvgValues(mBwAccKbps, mCount))
3016                     .append("\n BwEst Error\n").append(printAvgValues(mBwEstErrorAcc, mCount))
3017                     .append("\n StaticBw Error\n").append(printAvgValues(mStaticBwErrorAcc, mCount))
3018                     .toString();
3019         }
3020 
printValues(int[] values)3021         private String printValues(int[] values) {
3022             StringBuilder sb = new StringBuilder();
3023             for (int k = 0; k < NUM_SIGNAL_LEVEL; k++) {
3024                 sb.append(" " + values[k]);
3025             }
3026             return sb.toString();
3027         }
3028 
printAvgValues(long[] stats, int[] count)3029         private String printAvgValues(long[] stats, int[] count) {
3030             StringBuilder sb = new StringBuilder();
3031             for (int k = 0; k < NUM_SIGNAL_LEVEL; k++) {
3032                 int avgStat = calculateAvg(stats[k], count[k]);
3033                 sb.append(" " + avgStat);
3034             }
3035             return sb.toString();
3036         }
3037 
writeBandwidthStats()3038         private BandwidthEstimatorStats.PerRat writeBandwidthStats() {
3039             BandwidthEstimatorStats.PerRat stats = new BandwidthEstimatorStats.PerRat();
3040             List<BandwidthEstimatorStats.PerLevel> levelList = new ArrayList<>();
3041             for (int level = 0; level < NUM_SIGNAL_LEVEL; level++) {
3042                 BandwidthEstimatorStats.PerLevel currStats = writeBandwidthStatsPerLevel(level);
3043                 if (currStats != null) {
3044                     levelList.add(currStats);
3045                 }
3046             }
3047             stats.rat = mRadioTechnology;
3048             stats.perLevel = levelList.toArray(new BandwidthEstimatorStats.PerLevel[0]);
3049             stats.nrMode = mNrMode;
3050             return stats;
3051         }
3052 
writeBandwidthStatsPerLevel(int level)3053         private BandwidthEstimatorStats.PerLevel writeBandwidthStatsPerLevel(int level) {
3054             int count = mCount[level];
3055             if (count > 0) {
3056                 BandwidthEstimatorStats.PerLevel stats = new BandwidthEstimatorStats.PerLevel();
3057                 stats.signalLevel = level;
3058                 stats.count = count;
3059                 stats.avgBwKbps = calculateAvg(mBwAccKbps[level], count);
3060                 stats.staticBwErrorPercent = calculateAvg(mStaticBwErrorAcc[level], count);
3061                 stats.bwEstErrorPercent = calculateAvg(mBwEstErrorAcc[level], count);
3062                 return stats;
3063             }
3064             return null;
3065         }
3066 
calculateAvg(long acc, int count)3067         private int calculateAvg(long acc, int count) {
3068             return (count > 0) ? (int) (acc / count) : 0;
3069         }
3070     }
3071 
3072 }
3073