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
17package com.android.internal.telephony.metrics;
18
19import android.os.SystemClock;
20import android.telephony.Rlog;
21import android.telephony.ServiceState;
22import android.telephony.TelephonyHistogram;
23import android.util.Base64;
24import android.util.SparseArray;
25
26import com.android.ims.ImsConfig;
27import com.android.ims.ImsReasonInfo;
28import com.android.ims.internal.ImsCallSession;
29import com.android.internal.telephony.PhoneConstants;
30import com.android.internal.telephony.RIL;
31import com.android.internal.telephony.RILConstants;
32import com.android.internal.telephony.SmsResponse;
33import com.android.internal.telephony.TelephonyProto;
34import com.android.internal.telephony.TelephonyProto.ImsCapabilities;
35import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
36import com.android.internal.telephony.TelephonyProto.RilDataCall;
37import com.android.internal.telephony.TelephonyProto.SmsSession;
38import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
39import com.android.internal.telephony.TelephonyProto.TelephonyEvent;
40import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
41import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCall;
42import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
43import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse.RilDataCallFailCause;
44import com.android.internal.telephony.TelephonyProto.TelephonyLog;
45import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
46import com.android.internal.telephony.TelephonyProto.TelephonySettings;
47import com.android.internal.telephony.TelephonyProto.TimeInterval;
48import com.android.internal.telephony.UUSInfo;
49import com.android.internal.telephony.dataconnection.DataCallResponse;
50import com.android.internal.telephony.imsphone.ImsPhoneCall;
51import com.android.internal.util.IndentingPrintWriter;
52
53import java.io.FileDescriptor;
54import java.io.PrintWriter;
55import java.util.ArrayDeque;
56import java.util.ArrayList;
57import java.util.Deque;
58import java.util.List;
59
60import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
61import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
62import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS;
63import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
64import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL;
65import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
66import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
67import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND;
68import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS;
69import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
70import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
71import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
72import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IP;
73import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
74import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IPV6;
75import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_PPP;
76import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_UNKNOWN;
77
78/**
79 * Telephony metrics holds all metrics events and convert it into telephony proto buf.
80 * @hide
81 */
82public class TelephonyMetrics {
83
84    private static final String TAG = TelephonyMetrics.class.getSimpleName();
85
86    private static final boolean DBG = true;
87    private static final boolean VDBG = false; // STOPSHIP if true
88
89    /** Maximum telephony events stored */
90    private static final int MAX_TELEPHONY_EVENTS = 1000;
91
92    /** Maximum call sessions stored */
93    private static final int MAX_COMPLETED_CALL_SESSIONS = 50;
94
95    /** Maximum sms sessions stored */
96    private static final int MAX_COMPLETED_SMS_SESSIONS = 500;
97
98    /** For reducing the timing precision for privacy purposes */
99    private static final int SESSION_START_PRECISION_MINUTES = 5;
100
101    /** The TelephonyMetrics singleton instance */
102    private static TelephonyMetrics sInstance;
103
104    /** Telephony events */
105    private final Deque<TelephonyEvent> mTelephonyEvents = new ArrayDeque<>();
106
107    /**
108     * In progress call sessions. Note that each phone can only have up to 1 in progress call
109     * session (might contains multiple calls). Having a sparse array in case we need to support
110     * DSDA in the future.
111     */
112    private final SparseArray<InProgressCallSession> mInProgressCallSessions = new SparseArray<>();
113
114    /** The completed call sessions */
115    private final Deque<TelephonyCallSession> mCompletedCallSessions = new ArrayDeque<>();
116
117    /** The in-progress SMS sessions. When finished, it will be moved into the completed sessions */
118    private final SparseArray<InProgressSmsSession> mInProgressSmsSessions = new SparseArray<>();
119
120    /** The completed SMS sessions */
121    private final Deque<SmsSession> mCompletedSmsSessions = new ArrayDeque<>();
122
123    /** Last service state. This is for injecting the base of a new log or a new call/sms session */
124    private final SparseArray<TelephonyServiceState> mLastServiceState = new SparseArray<>();
125
126    /**
127     * Last ims capabilities. This is for injecting the base of a new log or a new call/sms
128     * session
129     */
130    private final SparseArray<ImsCapabilities> mLastImsCapabilities = new SparseArray<>();
131
132    /**
133     * Last IMS connection state. This is for injecting the base of a new log or a new call/sms
134     * session
135     */
136    private final SparseArray<ImsConnectionState> mLastImsConnectionState = new SparseArray<>();
137
138    /** The start system time of the TelephonyLog in milliseconds*/
139    private long mStartSystemTimeMs;
140
141    /** The start elapsed time of the TelephonyLog in milliseconds*/
142    private long mStartElapsedTimeMs;
143
144    /** Indicating if some of the telephony events are dropped in this log */
145    private boolean mTelephonyEventsDropped = false;
146
147    public TelephonyMetrics() {
148        reset();
149    }
150
151    /**
152     * Get the singleton instance of telephony metrics.
153     *
154     * @return The instance
155     */
156    public synchronized static TelephonyMetrics getInstance() {
157        if (sInstance == null) {
158            sInstance = new TelephonyMetrics();
159        }
160
161        return sInstance;
162    }
163
164    /**
165     * Dump the state of various objects, add calls to other objects as desired.
166     *
167     * @param fd File descriptor
168     * @param pw Print writer
169     * @param args Arguments
170     */
171    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
172        if (args != null && args.length > 0) {
173            switch (args[0]) {
174                case "--metrics":
175                    printAllMetrics(pw);
176                    break;
177                case "--metricsproto":
178                    pw.println(convertProtoToBase64String(buildProto()));
179                    reset();
180                    break;
181            }
182        }
183    }
184
185    /**
186     * Convert the telephony event to string
187     *
188     * @param event The event in integer
189     * @return The event in string
190     */
191    private static String telephonyEventToString(int event) {
192        switch (event) {
193            case TelephonyEvent.Type.UNKNOWN:
194                return "UNKNOWN";
195            case TelephonyEvent.Type.SETTINGS_CHANGED:
196                return "SETTINGS_CHANGED";
197            case TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED:
198                return "RIL_SERVICE_STATE_CHANGED";
199            case TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED:
200                return "IMS_CONNECTION_STATE_CHANGED";
201            case TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED:
202                return "IMS_CAPABILITIES_CHANGED";
203            case TelephonyEvent.Type.DATA_CALL_SETUP:
204                return "DATA_CALL_SETUP";
205            case TelephonyEvent.Type.DATA_CALL_SETUP_RESPONSE:
206                return "DATA_CALL_SETUP_RESPONSE";
207            case TelephonyEvent.Type.DATA_CALL_LIST_CHANGED:
208                return "DATA_CALL_LIST_CHANGED";
209            case TelephonyEvent.Type.DATA_CALL_DEACTIVATE:
210                return "DATA_CALL_DEACTIVATE";
211            case TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE:
212                return "DATA_CALL_DEACTIVATE_RESPONSE";
213            case TelephonyEvent.Type.DATA_STALL_ACTION:
214                return "DATA_STALL_ACTION";
215            case TelephonyEvent.Type.MODEM_RESTART:
216                return "MODEM_RESTART";
217            default:
218                return Integer.toString(event);
219        }
220    }
221
222    /**
223     * Convert the call session event into string
224     *
225     * @param event The event in integer
226     * @return The event in String
227     */
228    private static String callSessionEventToString(int event) {
229        switch (event) {
230            case TelephonyCallSession.Event.Type.EVENT_UNKNOWN:
231                return "EVENT_UNKNOWN";
232            case TelephonyCallSession.Event.Type.SETTINGS_CHANGED:
233                return "SETTINGS_CHANGED";
234            case TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED:
235                return "RIL_SERVICE_STATE_CHANGED";
236            case TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED:
237                return "IMS_CONNECTION_STATE_CHANGED";
238            case TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED:
239                return "IMS_CAPABILITIES_CHANGED";
240            case TelephonyCallSession.Event.Type.DATA_CALL_LIST_CHANGED:
241                return "DATA_CALL_LIST_CHANGED";
242            case TelephonyCallSession.Event.Type.RIL_REQUEST:
243                return "RIL_REQUEST";
244            case TelephonyCallSession.Event.Type.RIL_RESPONSE:
245                return "RIL_RESPONSE";
246            case TelephonyCallSession.Event.Type.RIL_CALL_RING:
247                return "RIL_CALL_RING";
248            case TelephonyCallSession.Event.Type.RIL_CALL_SRVCC:
249                return "RIL_CALL_SRVCC";
250            case TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED:
251                return "RIL_CALL_LIST_CHANGED";
252            case TelephonyCallSession.Event.Type.IMS_COMMAND:
253                return "IMS_COMMAND";
254            case TelephonyCallSession.Event.Type.IMS_COMMAND_RECEIVED:
255                return "IMS_COMMAND_RECEIVED";
256            case TelephonyCallSession.Event.Type.IMS_COMMAND_FAILED:
257                return "IMS_COMMAND_FAILED";
258            case TelephonyCallSession.Event.Type.IMS_COMMAND_COMPLETE:
259                return "IMS_COMMAND_COMPLETE";
260            case TelephonyCallSession.Event.Type.IMS_CALL_RECEIVE:
261                return "IMS_CALL_RECEIVE";
262            case TelephonyCallSession.Event.Type.IMS_CALL_STATE_CHANGED:
263                return "IMS_CALL_STATE_CHANGED";
264            case TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED:
265                return "IMS_CALL_TERMINATED";
266            case TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER:
267                return "IMS_CALL_HANDOVER";
268            case TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER_FAILED:
269                return "IMS_CALL_HANDOVER_FAILED";
270            case TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED:
271                return "PHONE_STATE_CHANGED";
272            case TelephonyCallSession.Event.Type.NITZ_TIME:
273                return "NITZ_TIME";
274            default:
275                return Integer.toString(event);
276        }
277    }
278
279    /**
280     * Convert the SMS session event into string
281     * @param event The event in integer
282     * @return The event in String
283     */
284    private static String smsSessionEventToString(int event) {
285        switch (event) {
286            case SmsSession.Event.Type.EVENT_UNKNOWN:
287                return "EVENT_UNKNOWN";
288            case SmsSession.Event.Type.SETTINGS_CHANGED:
289                return "SETTINGS_CHANGED";
290            case SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED:
291                return "RIL_SERVICE_STATE_CHANGED";
292            case SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED:
293                return "IMS_CONNECTION_STATE_CHANGED";
294            case SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED:
295                return "IMS_CAPABILITIES_CHANGED";
296            case SmsSession.Event.Type.DATA_CALL_LIST_CHANGED:
297                return "DATA_CALL_LIST_CHANGED";
298            case SmsSession.Event.Type.SMS_SEND:
299                return "SMS_SEND";
300            case SmsSession.Event.Type.SMS_SEND_RESULT:
301                return "SMS_SEND_RESULT";
302            case SmsSession.Event.Type.SMS_RECEIVED:
303                return "SMS_RECEIVED";
304            default:
305                return Integer.toString(event);
306        }
307    }
308
309    /**
310     * Print all metrics data for debugging purposes
311     *
312     * @param rawWriter Print writer
313     */
314    private synchronized void printAllMetrics(PrintWriter rawWriter) {
315        final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, "  ");
316
317        pw.println("Telephony metrics proto:");
318        pw.println("------------------------------------------");
319        pw.println("Telephony events:");
320        pw.increaseIndent();
321        for (TelephonyEvent event : mTelephonyEvents) {
322            if (event.hasTimestampMillis()) {
323                pw.print(event.getTimestampMillis());
324                pw.print(" [");
325                if (event.hasPhoneId()) pw.print(event.getPhoneId());
326                pw.print("] ");
327
328                if (event.hasType()) {
329                    pw.print("T=");
330                    if (event.getType() == TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED) {
331                        pw.print(telephonyEventToString(event.getType())
332                                + "(" + event.serviceState.getDataRat() + ")");
333                    } else {
334                        pw.print(telephonyEventToString(event.getType()));
335                    }
336                }
337                pw.println("");
338            }
339        }
340
341        pw.decreaseIndent();
342        pw.println("Call sessions:");
343        pw.increaseIndent();
344
345        for (TelephonyCallSession callSession : mCompletedCallSessions) {
346            if (callSession.hasStartTimeMinutes()) {
347                pw.println("Start time in minutes: " + callSession.getStartTimeMinutes());
348            }
349            if (callSession.hasEventsDropped()) {
350                pw.println("Events dropped: " + callSession.getEventsDropped());
351            }
352            pw.println("Events: ");
353            pw.increaseIndent();
354            for (TelephonyCallSession.Event event : callSession.events) {
355                pw.print(event.getDelay());
356                pw.print(" T=");
357                if (event.getType() == TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) {
358                    pw.println(callSessionEventToString(event.getType())
359                            + "(" + event.serviceState.getDataRat() + ")");
360                } else {
361                    pw.println(callSessionEventToString(event.getType()));
362                }
363            }
364            pw.decreaseIndent();
365        }
366
367        pw.decreaseIndent();
368        pw.println("Sms sessions:");
369        pw.increaseIndent();
370
371        int count = 0;
372        for (SmsSession smsSession : mCompletedSmsSessions) {
373            count++;
374            if (smsSession.hasStartTimeMinutes()) {
375                pw.print("[" + count + "] Start time in minutes: "
376                        + smsSession.getStartTimeMinutes());
377            }
378            if (smsSession.hasEventsDropped()) {
379                pw.println(", events dropped: " + smsSession.getEventsDropped());
380            }
381            pw.println("Events: ");
382            pw.increaseIndent();
383            for (SmsSession.Event event : smsSession.events) {
384                pw.print(event.getDelay());
385                pw.print(" T=");
386                pw.println(smsSessionEventToString(event.getType()));
387            }
388            pw.decreaseIndent();
389        }
390
391        pw.decreaseIndent();
392    }
393
394    /**
395     * Convert the telephony proto into Base-64 encoded string
396     *
397     * @param proto Telephony proto
398     * @return Encoded string
399     */
400    private static String convertProtoToBase64String(TelephonyLog proto) {
401        return Base64.encodeToString(
402                TelephonyProto.TelephonyLog.toByteArray(proto), Base64.DEFAULT);
403    }
404
405    /**
406     * Reset all events and sessions
407     */
408    private synchronized void reset() {
409        mTelephonyEvents.clear();
410        mCompletedCallSessions.clear();
411        mCompletedSmsSessions.clear();
412
413        mTelephonyEventsDropped = false;
414
415        mStartSystemTimeMs = System.currentTimeMillis();
416        mStartElapsedTimeMs = SystemClock.elapsedRealtime();
417
418        // Insert the last known service state, ims capabilities, and ims connection states as the
419        // base.
420        for (int i = 0; i < mLastServiceState.size(); i++) {
421            final int key = mLastServiceState.keyAt(i);
422
423            TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
424                    .setServiceState(mLastServiceState.get(key)).build();
425            addTelephonyEvent(event);
426        }
427
428        for (int i = 0; i < mLastImsCapabilities.size(); i++) {
429            final int key = mLastImsCapabilities.keyAt(i);
430
431            TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
432                    .setImsCapabilities(mLastImsCapabilities.get(key)).build();
433            addTelephonyEvent(event);
434        }
435
436        for (int i = 0; i < mLastImsConnectionState.size(); i++) {
437            final int key = mLastImsConnectionState.keyAt(i);
438
439            TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key)
440                    .setImsConnectionState(mLastImsConnectionState.get(key)).build();
441            addTelephonyEvent(event);
442        }
443    }
444
445    /**
446     * Build the telephony proto
447     *
448     * @return Telephony proto
449     */
450    private synchronized TelephonyLog buildProto() {
451
452        TelephonyLog log = new TelephonyLog();
453        // Build telephony events
454        log.events = new TelephonyEvent[mTelephonyEvents.size()];
455        mTelephonyEvents.toArray(log.events);
456        log.setEventsDropped(mTelephonyEventsDropped);
457
458        // Build call sessions
459        log.callSessions = new TelephonyCallSession[mCompletedCallSessions.size()];
460        mCompletedCallSessions.toArray(log.callSessions);
461
462        // Build SMS sessions
463        log.smsSessions = new SmsSession[mCompletedSmsSessions.size()];
464        mCompletedSmsSessions.toArray(log.smsSessions);
465
466        // Build histogram. Currently we only support RIL histograms.
467        List<TelephonyHistogram> rilHistograms = RIL.getTelephonyRILTimingHistograms();
468        log.histograms = new TelephonyProto.TelephonyHistogram[rilHistograms.size()];
469        for (int i = 0; i < rilHistograms.size(); i++) {
470            log.histograms[i] = new TelephonyProto.TelephonyHistogram();
471            TelephonyHistogram rilHistogram = rilHistograms.get(i);
472            TelephonyProto.TelephonyHistogram histogramProto = log.histograms[i];
473
474            histogramProto.setCategory(rilHistogram.getCategory());
475            histogramProto.setId(rilHistogram.getId());
476            histogramProto.setMinTimeMillis(rilHistogram.getMinTime());
477            histogramProto.setMaxTimeMillis(rilHistogram.getMaxTime());
478            histogramProto.setAvgTimeMillis(rilHistogram.getAverageTime());
479            histogramProto.setCount(rilHistogram.getSampleCount());
480            histogramProto.setBucketCount(rilHistogram.getBucketCount());
481            histogramProto.bucketEndPoints = rilHistogram.getBucketEndPoints();
482            histogramProto.bucketCounters = rilHistogram.getBucketCounters();
483        }
484
485        // Log the starting system time
486        log.startTime = new TelephonyProto.Time();
487        log.startTime.setSystemTimestampMillis(mStartSystemTimeMs);
488        log.startTime.setElapsedTimestampMillis(mStartElapsedTimeMs);
489
490        log.endTime = new TelephonyProto.Time();
491        log.endTime.setSystemTimestampMillis(System.currentTimeMillis());
492        log.endTime.setElapsedTimestampMillis(SystemClock.elapsedRealtime());
493
494        return log;
495    }
496
497    /**
498     * Reduce precision to meet privacy requirements.
499     *
500     * @param timestamp timestamp in milliseconds
501     * @return Precision reduced timestamp in minutes
502     */
503    static int roundSessionStart(long timestamp) {
504        return (int) ((timestamp) / (MINUTE_IN_MILLIS * SESSION_START_PRECISION_MINUTES)
505                * (SESSION_START_PRECISION_MINUTES));
506    }
507
508    /**
509     * Get the time interval with reduced prevision
510     *
511     * @param previousTimestamp Previous timestamp in milliseconds
512     * @param currentTimestamp Current timestamp in milliseconds
513     * @return The time interval
514     */
515    static int toPrivacyFuzzedTimeInterval(long previousTimestamp, long currentTimestamp) {
516        long diff = currentTimestamp - previousTimestamp;
517        if (diff < 0) {
518            return TimeInterval.TI_UNKNOWN;
519        } else if (diff <= 10) {
520            return TimeInterval.TI_10_MILLIS;
521        } else if (diff <= 20) {
522            return TimeInterval.TI_20_MILLIS;
523        } else if (diff <= 50) {
524            return TimeInterval.TI_50_MILLIS;
525        } else if (diff <= 100) {
526            return TimeInterval.TI_100_MILLIS;
527        } else if (diff <= 200) {
528            return TimeInterval.TI_200_MILLIS;
529        } else if (diff <= 500) {
530            return TimeInterval.TI_500_MILLIS;
531        } else if (diff <= 1000) {
532            return TimeInterval.TI_1_SEC;
533        } else if (diff <= 2000) {
534            return TimeInterval.TI_2_SEC;
535        } else if (diff <= 5000) {
536            return TimeInterval.TI_5_SEC;
537        } else if (diff <= 10000) {
538            return TimeInterval.TI_10_SEC;
539        } else if (diff <= 30000) {
540            return TimeInterval.TI_30_SEC;
541        } else if (diff <= 60000) {
542            return TimeInterval.TI_1_MINUTE;
543        } else if (diff <= 180000) {
544            return TimeInterval.TI_3_MINUTES;
545        } else if (diff <= 600000) {
546            return TimeInterval.TI_10_MINUTES;
547        } else if (diff <= 1800000) {
548            return TimeInterval.TI_30_MINUTES;
549        } else if (diff <= 3600000) {
550            return TimeInterval.TI_1_HOUR;
551        } else if (diff <= 7200000) {
552            return TimeInterval.TI_2_HOURS;
553        } else if (diff <= 14400000) {
554            return TimeInterval.TI_4_HOURS;
555        } else {
556            return TimeInterval.TI_MANY_HOURS;
557        }
558    }
559
560    /**
561     * Convert the service state into service state proto
562     *
563     * @param serviceState Service state
564     * @return Service state proto
565     */
566    private TelephonyServiceState toServiceStateProto(ServiceState serviceState) {
567        TelephonyServiceState ssProto = new TelephonyServiceState();
568
569        ssProto.setVoiceRoamingType(serviceState.getVoiceRoamingType());
570        ssProto.setDataRoamingType(serviceState.getDataRoamingType());
571
572        ssProto.voiceOperator = new TelephonyServiceState.TelephonyOperator();
573
574        if (serviceState.getVoiceOperatorAlphaLong() != null) {
575            ssProto.voiceOperator.setAlphaLong(serviceState.getVoiceOperatorAlphaLong());
576        }
577
578        if (serviceState.getVoiceOperatorAlphaShort() != null) {
579            ssProto.voiceOperator.setAlphaShort(serviceState.getVoiceOperatorAlphaShort());
580        }
581
582        if (serviceState.getVoiceOperatorNumeric() != null) {
583            ssProto.voiceOperator.setNumeric(serviceState.getVoiceOperatorNumeric());
584        }
585
586        ssProto.dataOperator = new TelephonyServiceState.TelephonyOperator();
587
588        if (serviceState.getDataOperatorAlphaLong() != null) {
589            ssProto.dataOperator.setAlphaLong(serviceState.getDataOperatorAlphaLong());
590        }
591
592        if (serviceState.getDataOperatorAlphaShort() != null) {
593            ssProto.dataOperator.setAlphaShort(serviceState.getDataOperatorAlphaShort());
594        }
595
596        if (serviceState.getDataOperatorNumeric() != null) {
597            ssProto.dataOperator.setNumeric(serviceState.getDataOperatorNumeric());
598        }
599
600        ssProto.setVoiceRat(serviceState.getRilVoiceRadioTechnology());
601        ssProto.setDataRat(serviceState.getRilDataRadioTechnology());
602        return ssProto;
603    }
604
605    /**
606     * Annotate the call session with events
607     *
608     * @param timestamp Event timestamp
609     * @param phoneId Phone id
610     * @param eventBuilder Call session event builder
611     */
612    private synchronized void annotateInProgressCallSession(long timestamp, int phoneId,
613                                                            CallSessionEventBuilder eventBuilder) {
614        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
615        if (callSession != null) {
616            callSession.addEvent(timestamp, eventBuilder);
617        }
618    }
619
620    /**
621     * Annotate the SMS session with events
622     *
623     * @param timestamp Event timestamp
624     * @param phoneId Phone id
625     * @param eventBuilder SMS session event builder
626     */
627    private synchronized void annotateInProgressSmsSession(long timestamp, int phoneId,
628                                                           SmsSessionEventBuilder eventBuilder) {
629        InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId);
630        if (smsSession != null) {
631            smsSession.addEvent(timestamp, eventBuilder);
632        }
633    }
634
635    /**
636     * Create the call session if there isn't any existing one
637     *
638     * @param phoneId Phone id
639     * @return The call session
640     */
641    private synchronized InProgressCallSession startNewCallSessionIfNeeded(int phoneId) {
642        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
643        if (callSession == null) {
644            if (VDBG) Rlog.v(TAG, "Starting a new call session on phone " + phoneId);
645            callSession = new InProgressCallSession(phoneId);
646            mInProgressCallSessions.append(phoneId, callSession);
647
648            // Insert the latest service state, ims capabilities, and ims connection states as the
649            // base.
650            TelephonyServiceState serviceState = mLastServiceState.get(phoneId);
651            if (serviceState != null) {
652                callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder(
653                        TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
654                        .setServiceState(serviceState));
655            }
656
657            ImsCapabilities imsCapabilities = mLastImsCapabilities.get(phoneId);
658            if (imsCapabilities != null) {
659                callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder(
660                        TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED)
661                        .setImsCapabilities(imsCapabilities));
662            }
663
664            ImsConnectionState imsConnectionState = mLastImsConnectionState.get(phoneId);
665            if (imsConnectionState != null) {
666                callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder(
667                        TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
668                        .setImsConnectionState(imsConnectionState));
669            }
670        }
671        return callSession;
672    }
673
674    /**
675     * Create the SMS session if there isn't any existing one
676     *
677     * @param phoneId Phone id
678     * @return The SMS session
679     */
680    private synchronized InProgressSmsSession startNewSmsSessionIfNeeded(int phoneId) {
681        InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId);
682        if (smsSession == null) {
683            if (VDBG) Rlog.v(TAG, "Starting a new sms session on phone " + phoneId);
684            smsSession = new InProgressSmsSession(phoneId);
685            mInProgressSmsSessions.append(phoneId, smsSession);
686
687            // Insert the latest service state, ims capabilities, and ims connection state as the
688            // base.
689            TelephonyServiceState serviceState = mLastServiceState.get(phoneId);
690            if (serviceState != null) {
691                smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder(
692                        TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
693                        .setServiceState(serviceState));
694            }
695
696            ImsCapabilities imsCapabilities = mLastImsCapabilities.get(phoneId);
697            if (imsCapabilities != null) {
698                smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder(
699                        SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED)
700                        .setImsCapabilities(imsCapabilities));
701            }
702
703            ImsConnectionState imsConnectionState = mLastImsConnectionState.get(phoneId);
704            if (imsConnectionState != null) {
705                smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder(
706                        SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
707                        .setImsConnectionState(imsConnectionState));
708            }
709        }
710        return smsSession;
711    }
712
713    /**
714     * Finish the call session and move it into the completed session
715     *
716     * @param inProgressCallSession The in progress call session
717     */
718    private synchronized void finishCallSession(InProgressCallSession inProgressCallSession) {
719        TelephonyCallSession callSession = new TelephonyCallSession();
720        callSession.events = new TelephonyCallSession.Event[inProgressCallSession.events.size()];
721        inProgressCallSession.events.toArray(callSession.events);
722        callSession.setStartTimeMinutes(inProgressCallSession.startSystemTimeMin);
723        callSession.setPhoneId(inProgressCallSession.phoneId);
724        callSession.setEventsDropped(inProgressCallSession.isEventsDropped());
725        if (mCompletedCallSessions.size() >= MAX_COMPLETED_CALL_SESSIONS) {
726            mCompletedCallSessions.removeFirst();
727        }
728        mCompletedCallSessions.add(callSession);
729        mInProgressCallSessions.remove(inProgressCallSession.phoneId);
730        if (VDBG) Rlog.v(TAG, "Call session finished");
731    }
732
733    /**
734     * Finish the SMS session and move it into the completed session
735     *
736     * @param inProgressSmsSession The in progress SMS session
737     */
738    private synchronized void finishSmsSessionIfNeeded(InProgressSmsSession inProgressSmsSession) {
739        if (inProgressSmsSession.getNumExpectedResponses() == 0) {
740            SmsSession smsSession = new SmsSession();
741            smsSession.events = new SmsSession.Event[inProgressSmsSession.events.size()];
742            inProgressSmsSession.events.toArray(smsSession.events);
743            smsSession.setStartTimeMinutes(inProgressSmsSession.startSystemTimeMin);
744            smsSession.setPhoneId(inProgressSmsSession.phoneId);
745            smsSession.setEventsDropped(inProgressSmsSession.isEventsDropped());
746            if (mCompletedSmsSessions.size() >= MAX_COMPLETED_SMS_SESSIONS) {
747                mCompletedSmsSessions.removeFirst();
748            }
749            mCompletedSmsSessions.add(smsSession);
750            mInProgressSmsSessions.remove(inProgressSmsSession.phoneId);
751            if (VDBG) Rlog.v(TAG, "SMS session finished");
752        }
753    }
754
755    /**
756     * Add telephony event into the queue
757     *
758     * @param event Telephony event
759     */
760    private synchronized void addTelephonyEvent(TelephonyEvent event) {
761        if (mTelephonyEvents.size() >= MAX_TELEPHONY_EVENTS) {
762            mTelephonyEvents.removeFirst();
763            mTelephonyEventsDropped = true;
764        }
765        mTelephonyEvents.add(event);
766    }
767
768    /**
769     * Write service changed event
770     *
771     * @param phoneId Phone id
772     * @param serviceState Service state
773     */
774    public synchronized void writeServiceStateChanged(int phoneId, ServiceState serviceState) {
775
776        TelephonyEvent event = new TelephonyEventBuilder(phoneId)
777                .setServiceState(toServiceStateProto(serviceState)).build();
778
779        mLastServiceState.put(phoneId, event.serviceState);
780        addTelephonyEvent(event);
781
782        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
783                new CallSessionEventBuilder(
784                        TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
785                        .setServiceState(event.serviceState));
786        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
787                new SmsSessionEventBuilder(
788                        SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
789                        .setServiceState(event.serviceState));
790    }
791
792    /**
793     * Write data stall event
794     *
795     * @param phoneId Phone id
796     * @param recoveryAction Data stall recovery action
797     */
798    public void writeDataStallEvent(int phoneId, int recoveryAction) {
799        addTelephonyEvent(new TelephonyEventBuilder(phoneId)
800                .setDataStallRecoveryAction(recoveryAction).build());
801    }
802
803    /**
804     * Write IMS feature settings changed event
805     *
806     * @param phoneId Phone id
807     * @param feature IMS feature
808     * @param network The IMS network type
809     * @param value The settings. 0 indicates disabled, otherwise enabled.
810     * @param status IMS operation status. See OperationStatusConstants for details.
811     */
812    public void writeImsSetFeatureValue(int phoneId, int feature, int network, int value,
813                                        int status) {
814        TelephonySettings s = new TelephonySettings();
815        switch (feature) {
816            case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE:
817                s.setIsEnhanced4GLteModeEnabled(value != 0);
818                break;
819            case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI:
820                s.setIsWifiCallingEnabled(value != 0);
821                break;
822            case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE:
823                s.setIsVtOverLteEnabled(value != 0);
824                break;
825            case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI:
826                s.setIsVtOverWifiEnabled(value != 0);
827                break;
828        }
829
830        TelephonyEvent event = new TelephonyEventBuilder(phoneId).setSettings(s).build();
831        addTelephonyEvent(event);
832
833        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
834                new CallSessionEventBuilder(TelephonyCallSession.Event.Type.SETTINGS_CHANGED)
835                        .setSettings(s));
836        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
837                new SmsSessionEventBuilder(SmsSession.Event.Type.SETTINGS_CHANGED)
838                        .setSettings(s));
839    }
840
841    /**
842     * Write the preferred network settings changed event
843     *
844     * @param phoneId Phone id
845     * @param networkType The preferred network
846     */
847    public void writeSetPreferredNetworkType(int phoneId, int networkType) {
848        TelephonySettings s = new TelephonySettings();
849        s.setPreferredNetworkMode(networkType);
850        addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSettings(s).build());
851    }
852
853    /**
854     * Write the IMS connection state changed event
855     *
856     * @param phoneId Phone id
857     * @param state IMS connection state
858     * @param reasonInfo The reason info. Only used for disconnected state.
859     */
860    public synchronized void writeOnImsConnectionState(int phoneId, int state,
861                                                       ImsReasonInfo reasonInfo) {
862        ImsConnectionState imsState = new ImsConnectionState();
863        imsState.setState(state);
864        mLastImsConnectionState.put(phoneId, imsState);
865
866        if (reasonInfo != null) {
867            TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo();
868
869            ri.setReasonCode(reasonInfo.getCode());
870            ri.setExtraCode(reasonInfo.getExtraCode());
871            String extraMessage = reasonInfo.getExtraMessage();
872            if (extraMessage != null) {
873                ri.setExtraMessage(extraMessage);
874            }
875
876            imsState.reasonInfo = ri;
877        }
878
879        TelephonyEvent event = new TelephonyEventBuilder(phoneId)
880                .setImsConnectionState(imsState).build();
881        addTelephonyEvent(event);
882
883        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
884                new CallSessionEventBuilder(
885                        TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
886                        .setImsConnectionState(event.imsConnectionState));
887        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
888                new SmsSessionEventBuilder(
889                        SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
890                        .setImsConnectionState(event.imsConnectionState));
891    }
892
893    /**
894     * Write the IMS capabilities changed event
895     *
896     * @param phoneId Phone id
897     * @param capabilities IMS capabilities array
898     */
899    public synchronized void writeOnImsCapabilities(int phoneId, boolean[] capabilities) {
900        ImsCapabilities cap = new ImsCapabilities();
901
902        cap.setVoiceOverLte(capabilities[0]);
903        cap.setVideoOverLte(capabilities[1]);
904        cap.setVoiceOverWifi(capabilities[2]);
905        cap.setVideoOverWifi(capabilities[3]);
906        cap.setUtOverLte(capabilities[4]);
907        cap.setUtOverWifi(capabilities[5]);
908
909        TelephonyEvent event = new TelephonyEventBuilder(phoneId).setImsCapabilities(cap).build();
910        mLastImsCapabilities.put(phoneId, cap);
911        addTelephonyEvent(event);
912
913        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
914                new CallSessionEventBuilder(
915                        TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED)
916                        .setImsCapabilities(event.imsCapabilities));
917        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
918                new SmsSessionEventBuilder(
919                        SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED)
920                        .setImsCapabilities(event.imsCapabilities));
921    }
922
923    /**
924     * Convert PDP type into the enumeration
925     *
926     * @param type PDP type
927     * @return The proto defined enumeration
928     */
929    private int toPdpType(String type) {
930        switch (type) {
931            case "IP":
932                return PDP_TYPE_IP;
933            case "IPV6":
934                return PDP_TYPE_IPV6;
935            case "IPV4V6":
936                return PDP_TYPE_IPV4V6;
937            case "PPP":
938                return PDP_TYPE_PPP;
939        }
940        Rlog.e(TAG, "Unknown type: " + type);
941        return PDP_UNKNOWN;
942    }
943
944    /**
945     * Write setup data call event
946     *
947     * @param phoneId Phone id
948     * @param rilSerial RIL request serial number
949     * @param radioTechnology The data call RAT
950     * @param profile Data profile
951     * @param apn APN in string
952     * @param authType Authentication type
953     * @param protocol Data connection protocol
954     */
955    public void writeRilSetupDataCall(int phoneId, int rilSerial, int radioTechnology, int profile,
956                                      String apn, int authType, String protocol) {
957
958        RilSetupDataCall setupDataCall = new RilSetupDataCall();
959        setupDataCall.setRat(radioTechnology);
960        setupDataCall.setDataProfile(profile + 1);  // off by 1 between proto and RIL constants.
961        if (apn != null) {
962            setupDataCall.setApn(apn);
963        }
964        if (protocol != null) {
965            setupDataCall.setType(toPdpType(protocol));
966        }
967
968        addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSetupDataCall(
969                setupDataCall).build());
970    }
971
972    /**
973     * Write data call deactivate event
974     *
975     * @param phoneId Phone id
976     * @param rilSerial RIL request serial number
977     * @param cid call id
978     * @param reason Deactivate reason
979     */
980    public void writeRilDeactivateDataCall(int phoneId, int rilSerial, int cid, int reason) {
981
982        RilDeactivateDataCall deactivateDataCall = new RilDeactivateDataCall();
983        deactivateDataCall.setCid(cid);
984        deactivateDataCall.setReason(reason + 1);
985
986        addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDeactivateDataCall(
987                deactivateDataCall).build());
988    }
989
990    /**
991     * Write get data call list event
992     *
993     * @param phoneId Phone id
994     * @param dcsList Data call list
995     */
996    public void writeRilDataCallList(int phoneId, ArrayList<DataCallResponse> dcsList) {
997
998        RilDataCall[] dataCalls = new RilDataCall[dcsList.size()];
999
1000        for (int i = 0; i < dcsList.size(); i++) {
1001            dataCalls[i] = new RilDataCall();
1002            dataCalls[i].setCid(dcsList.get(i).cid);
1003            if (dcsList.get(i).ifname != null) {
1004                dataCalls[i].setIframe(dcsList.get(i).ifname);
1005            }
1006            if (dcsList.get(i).type != null) {
1007                dataCalls[i].setType(toPdpType(dcsList.get(i).type));
1008            }
1009        }
1010
1011        addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDataCalls(dataCalls).build());
1012    }
1013
1014    /**
1015     * Write dial event
1016     *
1017     * @param phoneId Phone id
1018     * @param rilSerial RIL request serial number
1019     * @param clirMode CLIR (Calling Line Identification Restriction) mode
1020     * @param uusInfo User-to-User signaling Info
1021     */
1022    public void writeRilDial(int phoneId, int rilSerial, int clirMode, UUSInfo uusInfo) {
1023
1024        InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
1025
1026        callSession.addEvent(callSession.startElapsedTimeMs,
1027                new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
1028                        .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL)
1029                        .setRilRequestId(rilSerial)
1030        );
1031    }
1032
1033    /**
1034     * Write incoming call event
1035     *
1036     * @param phoneId Phone id
1037     * @param response Unused today
1038     */
1039    public void writeRilCallRing(int phoneId, char[] response) {
1040        InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
1041
1042        callSession.addEvent(callSession.startElapsedTimeMs,
1043                new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_CALL_RING));
1044    }
1045
1046    /**
1047     * Write call hangup event
1048     *
1049     * @param phoneId Phone id
1050     * @param rilSerial RIL request serial number
1051     * @param callId Call id
1052     */
1053    public void writeRilHangup(int phoneId, int rilSerial, int callId) {
1054        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1055        if (callSession == null) {
1056            Rlog.e(TAG, "Call session is missing");
1057        } else {
1058            callSession.addEvent(
1059                    new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
1060                            .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP)
1061                            .setRilRequestId(rilSerial)
1062                            .setCallIndex(callId));
1063        }
1064    }
1065
1066    /**
1067     * Write call answer event
1068     *
1069     * @param phoneId Phone id
1070     * @param rilSerial RIL request serial number
1071     */
1072    public void writeRilAnswer(int phoneId, int rilSerial) {
1073        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1074        if (callSession == null) {
1075            Rlog.e(TAG, "Call session is missing");
1076        } else {
1077            callSession.addEvent(
1078                    new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
1079                            .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_ANSWER)
1080                            .setRilRequestId(rilSerial));
1081        }
1082    }
1083
1084    /**
1085     * Write IMS call SRVCC event
1086     *
1087     * @param phoneId Phone id
1088     * @param rilSrvccState SRVCC state
1089     */
1090    public void writeRilSrvcc(int phoneId, int rilSrvccState) {
1091        InProgressCallSession callSession =  mInProgressCallSessions.get(phoneId);
1092        if (callSession == null) {
1093            Rlog.e(TAG, "Call session is missing");
1094        } else {
1095            callSession.addEvent(
1096                    new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_CALL_SRVCC)
1097                            .setSrvccState(rilSrvccState + 1));
1098        }
1099    }
1100
1101    /**
1102     * Convert RIL request into proto defined RIL request
1103     *
1104     * @param r RIL request
1105     * @return RIL request defined in call session proto
1106     */
1107    private int toCallSessionRilRequest(int r) {
1108        switch (r) {
1109            case RILConstants.RIL_REQUEST_DIAL:
1110                return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL;
1111
1112            case RILConstants.RIL_REQUEST_ANSWER:
1113                return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_ANSWER;
1114
1115            case RILConstants.RIL_REQUEST_HANGUP:
1116            case RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
1117            case RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
1118                return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP;
1119
1120            case RILConstants.RIL_REQUEST_SET_CALL_WAITING:
1121                return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_SET_CALL_WAITING;
1122
1123            case RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE:
1124                return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE;
1125
1126            case RILConstants.RIL_REQUEST_CDMA_FLASH:
1127                return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_CDMA_FLASH;
1128
1129            case RILConstants.RIL_REQUEST_CONFERENCE:
1130                return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_CONFERENCE;
1131        }
1132        Rlog.e(TAG, "Unknown RIL request: " + r);
1133        return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_UNKNOWN;
1134    }
1135
1136    /**
1137     * Write setup data call response event
1138     *
1139     * @param phoneId Phone id
1140     * @param rilSerial RIL request serial number
1141     * @param rilError RIL error
1142     * @param rilRequest RIL request
1143     * @param response Data call response
1144     */
1145    private void writeOnSetupDataCallResponse(int phoneId, int rilSerial, int rilError,
1146                                              int rilRequest, DataCallResponse response) {
1147
1148        RilSetupDataCallResponse setupDataCallResponse = new RilSetupDataCallResponse();
1149        RilDataCall dataCall = new RilDataCall();
1150
1151        if (response != null) {
1152            setupDataCallResponse.setStatus(
1153                    response.status == 0 ? RilDataCallFailCause.PDP_FAIL_NONE : response.status);
1154            setupDataCallResponse.setSuggestedRetryTimeMillis(response.suggestedRetryTime);
1155
1156            dataCall.setCid(response.cid);
1157            if (response.type != null) {
1158                dataCall.setType(toPdpType(response.type));
1159            }
1160
1161            if (response.ifname != null) {
1162                dataCall.setIframe(response.ifname);
1163            }
1164        }
1165        setupDataCallResponse.call = dataCall;
1166
1167        addTelephonyEvent(new TelephonyEventBuilder(phoneId)
1168                .setSetupDataCallResponse(setupDataCallResponse).build());
1169    }
1170
1171    /**
1172     * Write call related solicited response event
1173     *
1174     * @param phoneId Phone id
1175     * @param rilSerial RIL request serial number
1176     * @param rilError RIL error
1177     * @param rilRequest RIL request
1178     */
1179    private void writeOnCallSolicitedResponse(int phoneId, int rilSerial, int rilError,
1180                                              int rilRequest) {
1181        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1182        if (callSession == null) {
1183            Rlog.e(TAG, "Call session is missing");
1184        } else {
1185            callSession.addEvent(new CallSessionEventBuilder(
1186                    TelephonyCallSession.Event.Type.RIL_RESPONSE)
1187                    .setRilRequest(toCallSessionRilRequest(rilRequest))
1188                    .setRilRequestId(rilSerial)
1189                    .setRilError(rilError));
1190        }
1191    }
1192
1193    /**
1194     * Write SMS related solicited response event
1195     *
1196     * @param phoneId Phone id
1197     * @param rilSerial RIL request serial number
1198     * @param rilError RIL error
1199     * @param response SMS response
1200     */
1201    private synchronized void writeOnSmsSolicitedResponse(int phoneId, int rilSerial, int rilError,
1202                                                          SmsResponse response) {
1203
1204        InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId);
1205        if (smsSession == null) {
1206            Rlog.e(TAG, "SMS session is missing");
1207        } else {
1208
1209            int errorCode = 0;
1210            if (response != null) {
1211                errorCode = response.mErrorCode;
1212            }
1213
1214            smsSession.addEvent(new SmsSessionEventBuilder(
1215                    SmsSession.Event.Type.SMS_SEND_RESULT)
1216                    .setErrorCode(errorCode)
1217                    .setRilErrno(rilError)
1218                    .setRilRequestId(rilSerial)
1219            );
1220
1221            smsSession.decreaseExpectedResponse();
1222            finishSmsSessionIfNeeded(smsSession);
1223        }
1224    }
1225
1226    /**
1227     * Write deactivate data call response event
1228     *
1229     * @param phoneId Phone id
1230     * @param rilError RIL error
1231     */
1232    private void writeOnDeactivateDataCallResponse(int phoneId, int rilError) {
1233        addTelephonyEvent(new TelephonyEventBuilder(phoneId)
1234                .setDeactivateDataCallResponse(rilError + 1).build());
1235    }
1236
1237    /**
1238     * Write RIL solicited response event
1239     *
1240     * @param phoneId Phone id
1241     * @param rilSerial RIL request serial number
1242     * @param rilError RIL error
1243     * @param rilRequest RIL request
1244     * @param ret The returned RIL response
1245     */
1246    public void writeOnRilSolicitedResponse(int phoneId, int rilSerial, int rilError,
1247                                            int rilRequest, Object ret) {
1248        switch (rilRequest) {
1249            case RIL_REQUEST_SETUP_DATA_CALL:
1250                DataCallResponse dataCall = (DataCallResponse) ret;
1251                writeOnSetupDataCallResponse(phoneId, rilSerial, rilError, rilRequest, dataCall);
1252                break;
1253            case RIL_REQUEST_DEACTIVATE_DATA_CALL:
1254                writeOnDeactivateDataCallResponse(phoneId, rilError);
1255                break;
1256            case RIL_REQUEST_HANGUP:
1257            case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
1258            case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
1259            case RIL_REQUEST_DIAL:
1260            case RIL_REQUEST_ANSWER:
1261                writeOnCallSolicitedResponse(phoneId, rilSerial, rilError, rilRequest);
1262                break;
1263            case RIL_REQUEST_SEND_SMS:
1264            case RIL_REQUEST_SEND_SMS_EXPECT_MORE:
1265            case RIL_REQUEST_CDMA_SEND_SMS:
1266            case RIL_REQUEST_IMS_SEND_SMS:
1267                SmsResponse smsResponse = (SmsResponse) ret;
1268                writeOnSmsSolicitedResponse(phoneId, rilSerial, rilError, smsResponse);
1269                break;
1270        }
1271    }
1272
1273    /**
1274     * Write phone state changed event
1275     *
1276     * @param phoneId Phone id
1277     * @param phoneState Phone state. See PhoneConstants.State for the details.
1278     */
1279    public void writePhoneState(int phoneId, PhoneConstants.State phoneState) {
1280        int state;
1281        switch (phoneState) {
1282            case IDLE:
1283                state = TelephonyCallSession.Event.PhoneState.STATE_IDLE;
1284                break;
1285            case RINGING:
1286                state = TelephonyCallSession.Event.PhoneState.STATE_RINGING;
1287                break;
1288            case OFFHOOK:
1289                state = TelephonyCallSession.Event.PhoneState.STATE_OFFHOOK;
1290                break;
1291            default:
1292                state = TelephonyCallSession.Event.PhoneState.STATE_UNKNOWN;
1293                break;
1294        }
1295
1296        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1297        if (callSession == null) {
1298            Rlog.e(TAG, "Call session is missing");
1299        } else {
1300            if (state == TelephonyCallSession.Event.PhoneState.STATE_IDLE) {
1301                finishCallSession(callSession);
1302            }
1303            callSession.addEvent(new CallSessionEventBuilder(
1304                    TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED)
1305                    .setPhoneState(state));
1306        }
1307    }
1308
1309    /**
1310     * Extracts the call ID from an ImsSession.
1311     *
1312     * @param session The session.
1313     * @return The call ID for the session, or -1 if none was found.
1314     */
1315    private int getCallId(ImsCallSession session) {
1316        if (session == null) {
1317            return -1;
1318        }
1319
1320        try {
1321            return Integer.parseInt(session.getCallId());
1322        } catch (NumberFormatException nfe) {
1323            return -1;
1324        }
1325    }
1326
1327    /**
1328     * Write IMS call state changed event
1329     *
1330     * @param phoneId Phone id
1331     * @param session IMS call session
1332     * @param callState IMS call state
1333     */
1334    public void writeImsCallState(int phoneId, ImsCallSession session,
1335                                  ImsPhoneCall.State callState) {
1336        int state;
1337        switch (callState) {
1338            case IDLE:
1339                state = TelephonyCallSession.Event.CallState.CALL_IDLE; break;
1340            case ACTIVE:
1341                state = TelephonyCallSession.Event.CallState.CALL_ACTIVE; break;
1342            case HOLDING:
1343                state = TelephonyCallSession.Event.CallState.CALL_HOLDING; break;
1344            case DIALING:
1345                state = TelephonyCallSession.Event.CallState.CALL_DIALING; break;
1346            case ALERTING:
1347                state = TelephonyCallSession.Event.CallState.CALL_ALERTING; break;
1348            case INCOMING:
1349                state = TelephonyCallSession.Event.CallState.CALL_INCOMING; break;
1350            case WAITING:
1351                state = TelephonyCallSession.Event.CallState.CALL_WAITING; break;
1352            case DISCONNECTED:
1353                state = TelephonyCallSession.Event.CallState.CALL_DISCONNECTED; break;
1354            case DISCONNECTING:
1355                state = TelephonyCallSession.Event.CallState.CALL_DISCONNECTING; break;
1356            default:
1357                state = TelephonyCallSession.Event.CallState.CALL_UNKNOWN; break;
1358        }
1359
1360        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1361        if (callSession == null) {
1362            Rlog.e(TAG, "Call session is missing");
1363        } else {
1364            callSession.addEvent(new CallSessionEventBuilder(
1365                    TelephonyCallSession.Event.Type.IMS_CALL_STATE_CHANGED)
1366                    .setCallIndex(getCallId(session))
1367                    .setCallState(state));
1368        }
1369    }
1370
1371    /**
1372     * Write IMS call start event
1373     *
1374     * @param phoneId Phone id
1375     * @param session IMS call session
1376     */
1377    public void writeOnImsCallStart(int phoneId, ImsCallSession session) {
1378        InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
1379
1380        callSession.addEvent(
1381                new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_COMMAND)
1382                        .setCallIndex(getCallId(session))
1383                        .setImsCommand(TelephonyCallSession.Event.ImsCommand.IMS_CMD_START));
1384    }
1385
1386    /**
1387     * Write IMS incoming call event
1388     *
1389     * @param phoneId Phone id
1390     * @param session IMS call session
1391     */
1392    public void writeOnImsCallReceive(int phoneId, ImsCallSession session) {
1393        writeOnImsCallStart(phoneId, session);
1394    }
1395
1396    /**
1397     * Write IMS command event
1398     *
1399     * @param phoneId Phone id
1400     * @param session IMS call session
1401     * @param command IMS command
1402     */
1403    public void writeOnImsCommand(int phoneId, ImsCallSession session, int command) {
1404
1405        InProgressCallSession callSession =  mInProgressCallSessions.get(phoneId);
1406        if (callSession == null) {
1407            Rlog.e(TAG, "Call session is missing");
1408        } else {
1409            callSession.addEvent(
1410                    new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_COMMAND)
1411                            .setCallIndex(getCallId(session))
1412                            .setImsCommand(command));
1413        }
1414    }
1415
1416    /**
1417     * Convert IMS reason info into proto
1418     *
1419     * @param reasonInfo IMS reason info
1420     * @return Converted proto
1421     */
1422    private TelephonyProto.ImsReasonInfo toImsReasonInfoProto(ImsReasonInfo reasonInfo) {
1423        TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo();
1424        if (reasonInfo != null) {
1425            ri.setReasonCode(reasonInfo.getCode());
1426            ri.setExtraCode(reasonInfo.getExtraCode());
1427            String extraMessage = reasonInfo.getExtraMessage();
1428            if (extraMessage != null) {
1429                ri.setExtraMessage(extraMessage);
1430            }
1431        }
1432        return ri;
1433    }
1434
1435    /**
1436     * Write IMS call end event
1437     *
1438     * @param phoneId Phone id
1439     * @param session IMS call session
1440     * @param reasonInfo Call end reason
1441     */
1442    public void writeOnImsCallTerminated(int phoneId, ImsCallSession session,
1443                                         ImsReasonInfo reasonInfo) {
1444        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1445        if (callSession == null) {
1446            Rlog.e(TAG, "Call session is missing");
1447        } else {
1448            callSession.addEvent(
1449                    new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED)
1450                            .setCallIndex(getCallId(session))
1451                            .setImsReasonInfo(toImsReasonInfoProto(reasonInfo)));
1452        }
1453    }
1454
1455    /**
1456     * Write IMS call hangover event
1457     *
1458     * @param phoneId Phone id
1459     * @param eventType hangover type
1460     * @param session IMS call session
1461     * @param srcAccessTech Hangover starting RAT
1462     * @param targetAccessTech Hangover destination RAT
1463     * @param reasonInfo Hangover reason
1464     */
1465    public void writeOnImsCallHandoverEvent(int phoneId, int eventType, ImsCallSession session,
1466                                            int srcAccessTech, int targetAccessTech,
1467                                            ImsReasonInfo reasonInfo) {
1468        InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
1469        if (callSession == null) {
1470            Rlog.e(TAG, "Call session is missing");
1471        } else {
1472            callSession.addEvent(
1473                    new CallSessionEventBuilder(eventType)
1474                            .setCallIndex(getCallId(session))
1475                            .setSrcAccessTech(srcAccessTech)
1476                            .setTargetAccessTech(targetAccessTech)
1477                            .setImsReasonInfo(toImsReasonInfoProto(reasonInfo)));
1478        }
1479    }
1480
1481    /**
1482     * Write Send SMS event
1483     *
1484     * @param phoneId Phone id
1485     * @param rilSerial RIL request serial number
1486     * @param tech SMS RAT
1487     * @param format SMS format. Either 3GPP or 3GPP2.
1488     */
1489    public void writeRilSendSms(int phoneId, int rilSerial, int tech, int format) {
1490        InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
1491
1492        smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND)
1493                .setTech(tech)
1494                .setRilRequestId(rilSerial)
1495                .setFormat(format)
1496        );
1497
1498        smsSession.increaseExpectedResponse();
1499    }
1500
1501    /**
1502     * Write incoming SMS event
1503     *
1504     * @param phoneId Phone id
1505     * @param tech SMS RAT
1506     * @param format SMS format. Either 3GPP or 3GPP2.
1507     */
1508    public void writeRilNewSms(int phoneId, int tech, int format) {
1509        InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId);
1510
1511        smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
1512                .setTech(tech)
1513                .setFormat(format)
1514        );
1515
1516        finishSmsSessionIfNeeded(smsSession);
1517    }
1518
1519    /**
1520     * Write NITZ event
1521     *
1522     * @param phoneId Phone id
1523     * @param timestamp NITZ time in milliseconds
1524     */
1525    public void writeNITZEvent(int phoneId, long timestamp) {
1526        TelephonyEvent event = new TelephonyEventBuilder(phoneId).setNITZ(timestamp).build();
1527        addTelephonyEvent(event);
1528
1529        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
1530                new CallSessionEventBuilder(
1531                        TelephonyCallSession.Event.Type.NITZ_TIME)
1532                        .setNITZ(timestamp));
1533    }
1534
1535    //TODO: Expand the proto in the future
1536    public void writeOnImsCallProgressing(int phoneId, ImsCallSession session) {}
1537    public void writeOnImsCallStarted(int phoneId, ImsCallSession session) {}
1538    public void writeOnImsCallStartFailed(int phoneId, ImsCallSession session,
1539                                          ImsReasonInfo reasonInfo) {}
1540    public void writeOnImsCallHeld(int phoneId, ImsCallSession session) {}
1541    public void writeOnImsCallHoldReceived(int phoneId, ImsCallSession session) {}
1542    public void writeOnImsCallHoldFailed(int phoneId, ImsCallSession session,
1543                                         ImsReasonInfo reasonInfo) {}
1544    public void writeOnImsCallResumed(int phoneId, ImsCallSession session) {}
1545    public void writeOnImsCallResumeReceived(int phoneId, ImsCallSession session) {}
1546    public void writeOnImsCallResumeFailed(int phoneId, ImsCallSession session,
1547                                           ImsReasonInfo reasonInfo) {}
1548    public void writeOnRilTimeoutResponse(int phoneId, int rilSerial, int rilRequest) {}
1549}
1550