Home | History | Annotate | Download | only in metrics
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.telephony.metrics;
     18 
     19 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
     20 
     21 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
     22 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS;
     23 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
     24 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL;
     25 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
     26 import static com.android.internal.telephony.RILConstants
     27         .RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
     28 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND;
     29 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS;
     30 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
     31 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
     32 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
     33 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IP;
     34 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
     35 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV6;
     36 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_PPP;
     37 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_UNKNOWN;
     38 
     39 import android.os.Build;
     40 import android.os.SystemClock;
     41 import android.telephony.Rlog;
     42 import android.telephony.ServiceState;
     43 import android.telephony.TelephonyHistogram;
     44 import android.util.Base64;
     45 import android.util.SparseArray;
     46 
     47 import com.android.ims.ImsConfig;
     48 import com.android.ims.ImsReasonInfo;
     49 import com.android.ims.internal.ImsCallSession;
     50 import com.android.internal.telephony.GsmCdmaConnection;
     51 import com.android.internal.telephony.PhoneConstants;
     52 import com.android.internal.telephony.RIL;
     53 import com.android.internal.telephony.RILConstants;
     54 import com.android.internal.telephony.SmsResponse;
     55 import com.android.internal.telephony.UUSInfo;
     56 import com.android.internal.telephony.dataconnection.DataCallResponse;
     57 import com.android.internal.telephony.imsphone.ImsPhoneCall;
     58 import com.android.internal.telephony.nano.TelephonyProto;
     59 import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
     60 import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
     61 import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
     62 import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
     63 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
     64 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.CallState;
     65 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall;
     66 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall.Type;
     67 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
     68 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
     69 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
     70 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
     71 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
     72 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse
     73         .RilDataCallFailCause;
     74 import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog;
     75 import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
     76 import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
     77 import com.android.internal.telephony.nano.TelephonyProto.TimeInterval;
     78 import com.android.internal.util.IndentingPrintWriter;
     79 
     80 import java.io.FileDescriptor;
     81 import java.io.PrintWriter;
     82 import java.util.ArrayDeque;
     83 import java.util.ArrayList;
     84 import java.util.Arrays;
     85 import java.util.Deque;
     86 import java.util.List;
     87 
     88 /**
     89  * Telephony metrics holds all metrics events and convert it into telephony proto buf.
     90  * @hide
     91  */
     92 public 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