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