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