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