| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony.metrics; |
| |
| import static android.text.format.DateUtils.MINUTE_IN_MILLIS; |
| |
| import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER; |
| import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS; |
| import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL; |
| import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL; |
| import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP; |
| import static com.android.internal.telephony.RILConstants |
| .RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND; |
| import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND; |
| import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS; |
| import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS; |
| import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE; |
| import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL; |
| import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IP; |
| import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6; |
| import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV6; |
| import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_PPP; |
| import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_UNKNOWN; |
| |
| import android.os.Build; |
| import android.os.SystemClock; |
| import android.telephony.Rlog; |
| import android.telephony.ServiceState; |
| import android.telephony.TelephonyHistogram; |
| import android.text.TextUtils; |
| import android.util.Base64; |
| import android.util.SparseArray; |
| |
| import com.android.ims.ImsConfig; |
| import com.android.ims.ImsReasonInfo; |
| import com.android.ims.internal.ImsCallSession; |
| import com.android.internal.telephony.GsmCdmaConnection; |
| import com.android.internal.telephony.PhoneConstants; |
| import com.android.internal.telephony.RIL; |
| import com.android.internal.telephony.RILConstants; |
| import com.android.internal.telephony.SmsResponse; |
| import com.android.internal.telephony.UUSInfo; |
| import com.android.internal.telephony.dataconnection.DataCallResponse; |
| import com.android.internal.telephony.imsphone.ImsPhoneCall; |
| import com.android.internal.telephony.nano.TelephonyProto; |
| import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities; |
| import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState; |
| import com.android.internal.telephony.nano.TelephonyProto.RilDataCall; |
| import com.android.internal.telephony.nano.TelephonyProto.SmsSession; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.CallState; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall.Type; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse |
| .RilDataCallFailCause; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings; |
| import com.android.internal.telephony.nano.TelephonyProto.TimeInterval; |
| import com.android.internal.util.IndentingPrintWriter; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Deque; |
| import java.util.List; |
| |
| /** |
| * Telephony metrics holds all metrics events and convert it into telephony proto buf. |
| * @hide |
| */ |
| public class TelephonyMetrics { |
| |
| private static final String TAG = TelephonyMetrics.class.getSimpleName(); |
| |
| private static final boolean DBG = true; |
| private static final boolean VDBG = false; // STOPSHIP if true |
| |
| /** Maximum telephony events stored */ |
| private static final int MAX_TELEPHONY_EVENTS = 1000; |
| |
| /** Maximum call sessions stored */ |
| private static final int MAX_COMPLETED_CALL_SESSIONS = 50; |
| |
| /** Maximum sms sessions stored */ |
| private static final int MAX_COMPLETED_SMS_SESSIONS = 500; |
| |
| /** For reducing the timing precision for privacy purposes */ |
| private static final int SESSION_START_PRECISION_MINUTES = 5; |
| |
| /** The TelephonyMetrics singleton instance */ |
| private static TelephonyMetrics sInstance; |
| |
| /** Telephony events */ |
| private final Deque<TelephonyEvent> mTelephonyEvents = new ArrayDeque<>(); |
| |
| /** |
| * In progress call sessions. Note that each phone can only have up to 1 in progress call |
| * session (might contains multiple calls). Having a sparse array in case we need to support |
| * DSDA in the future. |
| */ |
| private final SparseArray<InProgressCallSession> mInProgressCallSessions = new SparseArray<>(); |
| |
| /** The completed call sessions */ |
| private final Deque<TelephonyCallSession> mCompletedCallSessions = new ArrayDeque<>(); |
| |
| /** The in-progress SMS sessions. When finished, it will be moved into the completed sessions */ |
| private final SparseArray<InProgressSmsSession> mInProgressSmsSessions = new SparseArray<>(); |
| |
| /** The completed SMS sessions */ |
| private final Deque<SmsSession> mCompletedSmsSessions = new ArrayDeque<>(); |
| |
| /** Last service state. This is for injecting the base of a new log or a new call/sms session */ |
| private final SparseArray<TelephonyServiceState> mLastServiceState = new SparseArray<>(); |
| |
| /** |
| * Last ims capabilities. This is for injecting the base of a new log or a new call/sms |
| * session |
| */ |
| private final SparseArray<ImsCapabilities> mLastImsCapabilities = new SparseArray<>(); |
| |
| /** |
| * Last IMS connection state. This is for injecting the base of a new log or a new call/sms |
| * session |
| */ |
| private final SparseArray<ImsConnectionState> mLastImsConnectionState = new SparseArray<>(); |
| |
| /** |
| * Last settings state. This is for deduping same settings event logged. |
| */ |
| private final SparseArray<TelephonySettings> mLastSettings = new SparseArray<>(); |
| |
| /** The start system time of the TelephonyLog in milliseconds*/ |
| private long mStartSystemTimeMs; |
| |
| /** The start elapsed time of the TelephonyLog in milliseconds*/ |
| private long mStartElapsedTimeMs; |
| |
| /** Indicating if some of the telephony events are dropped in this log */ |
| private boolean mTelephonyEventsDropped = false; |
| |
| public TelephonyMetrics() { |
| reset(); |
| } |
| |
| /** |
| * Get the singleton instance of telephony metrics. |
| * |
| * @return The instance |
| */ |
| public synchronized static TelephonyMetrics getInstance() { |
| if (sInstance == null) { |
| sInstance = new TelephonyMetrics(); |
| } |
| |
| return sInstance; |
| } |
| |
| /** |
| * Dump the state of various objects, add calls to other objects as desired. |
| * |
| * @param fd File descriptor |
| * @param pw Print writer |
| * @param args Arguments |
| */ |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| if (args != null && args.length > 0) { |
| switch (args[0]) { |
| case "--metrics": |
| printAllMetrics(pw); |
| break; |
| case "--metricsproto": |
| pw.println(convertProtoToBase64String(buildProto())); |
| reset(); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Convert the telephony event to string |
| * |
| * @param event The event in integer |
| * @return The event in string |
| */ |
| private static String telephonyEventToString(int event) { |
| switch (event) { |
| case TelephonyEvent.Type.UNKNOWN: |
| return "UNKNOWN"; |
| case TelephonyEvent.Type.SETTINGS_CHANGED: |
| return "SETTINGS_CHANGED"; |
| case TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED: |
| return "RIL_SERVICE_STATE_CHANGED"; |
| case TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED: |
| return "IMS_CONNECTION_STATE_CHANGED"; |
| case TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED: |
| return "IMS_CAPABILITIES_CHANGED"; |
| case TelephonyEvent.Type.DATA_CALL_SETUP: |
| return "DATA_CALL_SETUP"; |
| case TelephonyEvent.Type.DATA_CALL_SETUP_RESPONSE: |
| return "DATA_CALL_SETUP_RESPONSE"; |
| case TelephonyEvent.Type.DATA_CALL_LIST_CHANGED: |
| return "DATA_CALL_LIST_CHANGED"; |
| case TelephonyEvent.Type.DATA_CALL_DEACTIVATE: |
| return "DATA_CALL_DEACTIVATE"; |
| case TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE: |
| return "DATA_CALL_DEACTIVATE_RESPONSE"; |
| case TelephonyEvent.Type.DATA_STALL_ACTION: |
| return "DATA_STALL_ACTION"; |
| case TelephonyEvent.Type.MODEM_RESTART: |
| return "MODEM_RESTART"; |
| default: |
| return Integer.toString(event); |
| } |
| } |
| |
| /** |
| * Convert the call session event into string |
| * |
| * @param event The event in integer |
| * @return The event in String |
| */ |
| private static String callSessionEventToString(int event) { |
| switch (event) { |
| case TelephonyCallSession.Event.Type.EVENT_UNKNOWN: |
| return "EVENT_UNKNOWN"; |
| case TelephonyCallSession.Event.Type.SETTINGS_CHANGED: |
| return "SETTINGS_CHANGED"; |
| case TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED: |
| return "RIL_SERVICE_STATE_CHANGED"; |
| case TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED: |
| return "IMS_CONNECTION_STATE_CHANGED"; |
| case TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED: |
| return "IMS_CAPABILITIES_CHANGED"; |
| case TelephonyCallSession.Event.Type.DATA_CALL_LIST_CHANGED: |
| return "DATA_CALL_LIST_CHANGED"; |
| case TelephonyCallSession.Event.Type.RIL_REQUEST: |
| return "RIL_REQUEST"; |
| case TelephonyCallSession.Event.Type.RIL_RESPONSE: |
| return "RIL_RESPONSE"; |
| case TelephonyCallSession.Event.Type.RIL_CALL_RING: |
| return "RIL_CALL_RING"; |
| case TelephonyCallSession.Event.Type.RIL_CALL_SRVCC: |
| return "RIL_CALL_SRVCC"; |
| case TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED: |
| return "RIL_CALL_LIST_CHANGED"; |
| case TelephonyCallSession.Event.Type.IMS_COMMAND: |
| return "IMS_COMMAND"; |
| case TelephonyCallSession.Event.Type.IMS_COMMAND_RECEIVED: |
| return "IMS_COMMAND_RECEIVED"; |
| case TelephonyCallSession.Event.Type.IMS_COMMAND_FAILED: |
| return "IMS_COMMAND_FAILED"; |
| case TelephonyCallSession.Event.Type.IMS_COMMAND_COMPLETE: |
| return "IMS_COMMAND_COMPLETE"; |
| case TelephonyCallSession.Event.Type.IMS_CALL_RECEIVE: |
| return "IMS_CALL_RECEIVE"; |
| case TelephonyCallSession.Event.Type.IMS_CALL_STATE_CHANGED: |
| return "IMS_CALL_STATE_CHANGED"; |
| case TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED: |
| return "IMS_CALL_TERMINATED"; |
| case TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER: |
| return "IMS_CALL_HANDOVER"; |
| case TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER_FAILED: |
| return "IMS_CALL_HANDOVER_FAILED"; |
| case TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED: |
| return "PHONE_STATE_CHANGED"; |
| case TelephonyCallSession.Event.Type.NITZ_TIME: |
| return "NITZ_TIME"; |
| default: |
| return Integer.toString(event); |
| } |
| } |
| |
| /** |
| * Convert the SMS session event into string |
| * @param event The event in integer |
| * @return The event in String |
| */ |
| private static String smsSessionEventToString(int event) { |
| switch (event) { |
| case SmsSession.Event.Type.EVENT_UNKNOWN: |
| return "EVENT_UNKNOWN"; |
| case SmsSession.Event.Type.SETTINGS_CHANGED: |
| return "SETTINGS_CHANGED"; |
| case SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED: |
| return "RIL_SERVICE_STATE_CHANGED"; |
| case SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED: |
| return "IMS_CONNECTION_STATE_CHANGED"; |
| case SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED: |
| return "IMS_CAPABILITIES_CHANGED"; |
| case SmsSession.Event.Type.DATA_CALL_LIST_CHANGED: |
| return "DATA_CALL_LIST_CHANGED"; |
| case SmsSession.Event.Type.SMS_SEND: |
| return "SMS_SEND"; |
| case SmsSession.Event.Type.SMS_SEND_RESULT: |
| return "SMS_SEND_RESULT"; |
| case SmsSession.Event.Type.SMS_RECEIVED: |
| return "SMS_RECEIVED"; |
| default: |
| return Integer.toString(event); |
| } |
| } |
| |
| /** |
| * Print all metrics data for debugging purposes |
| * |
| * @param rawWriter Print writer |
| */ |
| private synchronized void printAllMetrics(PrintWriter rawWriter) { |
| final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, " "); |
| |
| pw.println("Telephony metrics proto:"); |
| pw.println("------------------------------------------"); |
| pw.println("Telephony events:"); |
| pw.increaseIndent(); |
| for (TelephonyEvent event : mTelephonyEvents) { |
| pw.print(event.timestampMillis); |
| pw.print(" ["); |
| pw.print(event.phoneId); |
| pw.print("] "); |
| |
| pw.print("T="); |
| if (event.type == TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED) { |
| pw.print(telephonyEventToString(event.type) |
| + "(" + event.serviceState.dataRat + ")"); |
| } else { |
| pw.print(telephonyEventToString(event.type)); |
| } |
| |
| pw.println(""); |
| } |
| |
| pw.decreaseIndent(); |
| pw.println("Call sessions:"); |
| pw.increaseIndent(); |
| |
| for (TelephonyCallSession callSession : mCompletedCallSessions) { |
| pw.println("Start time in minutes: " + callSession.startTimeMinutes); |
| pw.println("Events dropped: " + callSession.eventsDropped); |
| |
| pw.println("Events: "); |
| pw.increaseIndent(); |
| for (TelephonyCallSession.Event event : callSession.events) { |
| pw.print(event.delay); |
| pw.print(" T="); |
| if (event.type == TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) { |
| pw.println(callSessionEventToString(event.type) |
| + "(" + event.serviceState.dataRat + ")"); |
| } else if (event.type == TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED) { |
| pw.println(callSessionEventToString(event.type)); |
| pw.increaseIndent(); |
| for (RilCall call : event.calls) { |
| pw.println(call.index + ". Type = " + call.type + " State = " |
| + call.state + " End Reason " + call.callEndReason |
| + " isMultiparty = " + call.isMultiparty); |
| } |
| pw.decreaseIndent(); |
| } else { |
| pw.println(callSessionEventToString(event.type)); |
| } |
| } |
| pw.decreaseIndent(); |
| } |
| |
| pw.decreaseIndent(); |
| pw.println("Sms sessions:"); |
| pw.increaseIndent(); |
| |
| int count = 0; |
| for (SmsSession smsSession : mCompletedSmsSessions) { |
| count++; |
| pw.print("[" + count + "] Start time in minutes: " |
| + smsSession.startTimeMinutes); |
| |
| if (smsSession.eventsDropped) { |
| pw.println(", events dropped: " + smsSession.eventsDropped); |
| } |
| pw.println("Events: "); |
| pw.increaseIndent(); |
| for (SmsSession.Event event : smsSession.events) { |
| pw.print(event.delay); |
| pw.print(" T="); |
| pw.println(smsSessionEventToString(event.type)); |
| } |
| pw.decreaseIndent(); |
| } |
| |
| pw.decreaseIndent(); |
| } |
| |
| /** |
| * Convert the telephony proto into Base-64 encoded string |
| * |
| * @param proto Telephony proto |
| * @return Encoded string |
| */ |
| private static String convertProtoToBase64String(TelephonyLog proto) { |
| return Base64.encodeToString( |
| TelephonyProto.TelephonyLog.toByteArray(proto), Base64.DEFAULT); |
| } |
| |
| /** |
| * Reset all events and sessions |
| */ |
| private synchronized void reset() { |
| mTelephonyEvents.clear(); |
| mCompletedCallSessions.clear(); |
| mCompletedSmsSessions.clear(); |
| |
| mTelephonyEventsDropped = false; |
| |
| mStartSystemTimeMs = System.currentTimeMillis(); |
| mStartElapsedTimeMs = SystemClock.elapsedRealtime(); |
| |
| // Insert the last known service state, ims capabilities, and ims connection states as the |
| // base. |
| for (int i = 0; i < mLastServiceState.size(); i++) { |
| final int key = mLastServiceState.keyAt(i); |
| |
| TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) |
| .setServiceState(mLastServiceState.get(key)).build(); |
| addTelephonyEvent(event); |
| } |
| |
| for (int i = 0; i < mLastImsCapabilities.size(); i++) { |
| final int key = mLastImsCapabilities.keyAt(i); |
| |
| TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) |
| .setImsCapabilities(mLastImsCapabilities.get(key)).build(); |
| addTelephonyEvent(event); |
| } |
| |
| for (int i = 0; i < mLastImsConnectionState.size(); i++) { |
| final int key = mLastImsConnectionState.keyAt(i); |
| |
| TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) |
| .setImsConnectionState(mLastImsConnectionState.get(key)).build(); |
| addTelephonyEvent(event); |
| } |
| } |
| |
| /** |
| * Build the telephony proto |
| * |
| * @return Telephony proto |
| */ |
| private synchronized TelephonyLog buildProto() { |
| |
| TelephonyLog log = new TelephonyLog(); |
| // Build telephony events |
| log.events = new TelephonyEvent[mTelephonyEvents.size()]; |
| mTelephonyEvents.toArray(log.events); |
| log.eventsDropped = mTelephonyEventsDropped; |
| |
| // Build call sessions |
| log.callSessions = new TelephonyCallSession[mCompletedCallSessions.size()]; |
| mCompletedCallSessions.toArray(log.callSessions); |
| |
| // Build SMS sessions |
| log.smsSessions = new SmsSession[mCompletedSmsSessions.size()]; |
| mCompletedSmsSessions.toArray(log.smsSessions); |
| |
| // Build histogram. Currently we only support RIL histograms. |
| List<TelephonyHistogram> rilHistograms = RIL.getTelephonyRILTimingHistograms(); |
| log.histograms = new TelephonyProto.TelephonyHistogram[rilHistograms.size()]; |
| for (int i = 0; i < rilHistograms.size(); i++) { |
| log.histograms[i] = new TelephonyProto.TelephonyHistogram(); |
| TelephonyHistogram rilHistogram = rilHistograms.get(i); |
| TelephonyProto.TelephonyHistogram histogramProto = log.histograms[i]; |
| |
| histogramProto.category = rilHistogram.getCategory(); |
| histogramProto.id = rilHistogram.getId(); |
| histogramProto.minTimeMillis = rilHistogram.getMinTime(); |
| histogramProto.maxTimeMillis = rilHistogram.getMaxTime(); |
| histogramProto.avgTimeMillis = rilHistogram.getAverageTime(); |
| histogramProto.count = rilHistogram.getSampleCount(); |
| histogramProto.bucketCount = rilHistogram.getBucketCount(); |
| histogramProto.bucketEndPoints = rilHistogram.getBucketEndPoints(); |
| histogramProto.bucketCounters = rilHistogram.getBucketCounters(); |
| } |
| |
| // Log the starting system time |
| log.startTime = new TelephonyProto.Time(); |
| log.startTime.systemTimestampMillis = mStartSystemTimeMs; |
| log.startTime.elapsedTimestampMillis = mStartElapsedTimeMs; |
| |
| log.endTime = new TelephonyProto.Time(); |
| log.endTime.systemTimestampMillis = System.currentTimeMillis(); |
| log.endTime.elapsedTimestampMillis = SystemClock.elapsedRealtime(); |
| |
| return log; |
| } |
| |
| /** |
| * Reduce precision to meet privacy requirements. |
| * |
| * @param timestamp timestamp in milliseconds |
| * @return Precision reduced timestamp in minutes |
| */ |
| static int roundSessionStart(long timestamp) { |
| return (int) ((timestamp) / (MINUTE_IN_MILLIS * SESSION_START_PRECISION_MINUTES) |
| * (SESSION_START_PRECISION_MINUTES)); |
| } |
| |
| /** |
| * Get the time interval with reduced prevision |
| * |
| * @param previousTimestamp Previous timestamp in milliseconds |
| * @param currentTimestamp Current timestamp in milliseconds |
| * @return The time interval |
| */ |
| static int toPrivacyFuzzedTimeInterval(long previousTimestamp, long currentTimestamp) { |
| long diff = currentTimestamp - previousTimestamp; |
| if (diff < 0) { |
| return TimeInterval.TI_UNKNOWN; |
| } else if (diff <= 10) { |
| return TimeInterval.TI_10_MILLIS; |
| } else if (diff <= 20) { |
| return TimeInterval.TI_20_MILLIS; |
| } else if (diff <= 50) { |
| return TimeInterval.TI_50_MILLIS; |
| } else if (diff <= 100) { |
| return TimeInterval.TI_100_MILLIS; |
| } else if (diff <= 200) { |
| return TimeInterval.TI_200_MILLIS; |
| } else if (diff <= 500) { |
| return TimeInterval.TI_500_MILLIS; |
| } else if (diff <= 1000) { |
| return TimeInterval.TI_1_SEC; |
| } else if (diff <= 2000) { |
| return TimeInterval.TI_2_SEC; |
| } else if (diff <= 5000) { |
| return TimeInterval.TI_5_SEC; |
| } else if (diff <= 10000) { |
| return TimeInterval.TI_10_SEC; |
| } else if (diff <= 30000) { |
| return TimeInterval.TI_30_SEC; |
| } else if (diff <= 60000) { |
| return TimeInterval.TI_1_MINUTE; |
| } else if (diff <= 180000) { |
| return TimeInterval.TI_3_MINUTES; |
| } else if (diff <= 600000) { |
| return TimeInterval.TI_10_MINUTES; |
| } else if (diff <= 1800000) { |
| return TimeInterval.TI_30_MINUTES; |
| } else if (diff <= 3600000) { |
| return TimeInterval.TI_1_HOUR; |
| } else if (diff <= 7200000) { |
| return TimeInterval.TI_2_HOURS; |
| } else if (diff <= 14400000) { |
| return TimeInterval.TI_4_HOURS; |
| } else { |
| return TimeInterval.TI_MANY_HOURS; |
| } |
| } |
| |
| /** |
| * Convert the service state into service state proto |
| * |
| * @param serviceState Service state |
| * @return Service state proto |
| */ |
| private TelephonyServiceState toServiceStateProto(ServiceState serviceState) { |
| TelephonyServiceState ssProto = new TelephonyServiceState(); |
| |
| ssProto.voiceRoamingType = serviceState.getVoiceRoamingType(); |
| ssProto.dataRoamingType = serviceState.getDataRoamingType(); |
| |
| ssProto.voiceOperator = new TelephonyServiceState.TelephonyOperator(); |
| |
| if (serviceState.getVoiceOperatorAlphaLong() != null) { |
| ssProto.voiceOperator.alphaLong = serviceState.getVoiceOperatorAlphaLong(); |
| } |
| |
| if (serviceState.getVoiceOperatorAlphaShort() != null) { |
| ssProto.voiceOperator.alphaShort = serviceState.getVoiceOperatorAlphaShort(); |
| } |
| |
| if (serviceState.getVoiceOperatorNumeric() != null) { |
| ssProto.voiceOperator.numeric = serviceState.getVoiceOperatorNumeric(); |
| } |
| |
| ssProto.dataOperator = new TelephonyServiceState.TelephonyOperator(); |
| |
| if (serviceState.getDataOperatorAlphaLong() != null) { |
| ssProto.dataOperator.alphaLong = serviceState.getDataOperatorAlphaLong(); |
| } |
| |
| if (serviceState.getDataOperatorAlphaShort() != null) { |
| ssProto.dataOperator.alphaShort = serviceState.getDataOperatorAlphaShort(); |
| } |
| |
| if (serviceState.getDataOperatorNumeric() != null) { |
| ssProto.dataOperator.numeric = serviceState.getDataOperatorNumeric(); |
| } |
| |
| ssProto.voiceRat = serviceState.getRilVoiceRadioTechnology(); |
| ssProto.dataRat = serviceState.getRilDataRadioTechnology(); |
| return ssProto; |
| } |
| |
| /** |
| * Annotate the call session with events |
| * |
| * @param timestamp Event timestamp |
| * @param phoneId Phone id |
| * @param eventBuilder Call session event builder |
| */ |
| private synchronized void annotateInProgressCallSession(long timestamp, int phoneId, |
| CallSessionEventBuilder eventBuilder) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession != null) { |
| callSession.addEvent(timestamp, eventBuilder); |
| } |
| } |
| |
| /** |
| * Annotate the SMS session with events |
| * |
| * @param timestamp Event timestamp |
| * @param phoneId Phone id |
| * @param eventBuilder SMS session event builder |
| */ |
| private synchronized void annotateInProgressSmsSession(long timestamp, int phoneId, |
| SmsSessionEventBuilder eventBuilder) { |
| InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId); |
| if (smsSession != null) { |
| smsSession.addEvent(timestamp, eventBuilder); |
| } |
| } |
| |
| /** |
| * Create the call session if there isn't any existing one |
| * |
| * @param phoneId Phone id |
| * @return The call session |
| */ |
| private synchronized InProgressCallSession startNewCallSessionIfNeeded(int phoneId) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| if (VDBG) Rlog.v(TAG, "Starting a new call session on phone " + phoneId); |
| callSession = new InProgressCallSession(phoneId); |
| mInProgressCallSessions.append(phoneId, callSession); |
| |
| // Insert the latest service state, ims capabilities, and ims connection states as the |
| // base. |
| TelephonyServiceState serviceState = mLastServiceState.get(phoneId); |
| if (serviceState != null) { |
| callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) |
| .setServiceState(serviceState)); |
| } |
| |
| ImsCapabilities imsCapabilities = mLastImsCapabilities.get(phoneId); |
| if (imsCapabilities != null) { |
| callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED) |
| .setImsCapabilities(imsCapabilities)); |
| } |
| |
| ImsConnectionState imsConnectionState = mLastImsConnectionState.get(phoneId); |
| if (imsConnectionState != null) { |
| callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED) |
| .setImsConnectionState(imsConnectionState)); |
| } |
| } |
| return callSession; |
| } |
| |
| /** |
| * Create the SMS session if there isn't any existing one |
| * |
| * @param phoneId Phone id |
| * @return The SMS session |
| */ |
| private synchronized InProgressSmsSession startNewSmsSessionIfNeeded(int phoneId) { |
| InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId); |
| if (smsSession == null) { |
| if (VDBG) Rlog.v(TAG, "Starting a new sms session on phone " + phoneId); |
| smsSession = new InProgressSmsSession(phoneId); |
| mInProgressSmsSessions.append(phoneId, smsSession); |
| |
| // Insert the latest service state, ims capabilities, and ims connection state as the |
| // base. |
| TelephonyServiceState serviceState = mLastServiceState.get(phoneId); |
| if (serviceState != null) { |
| smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder( |
| TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) |
| .setServiceState(serviceState)); |
| } |
| |
| ImsCapabilities imsCapabilities = mLastImsCapabilities.get(phoneId); |
| if (imsCapabilities != null) { |
| smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder( |
| SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED) |
| .setImsCapabilities(imsCapabilities)); |
| } |
| |
| ImsConnectionState imsConnectionState = mLastImsConnectionState.get(phoneId); |
| if (imsConnectionState != null) { |
| smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder( |
| SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED) |
| .setImsConnectionState(imsConnectionState)); |
| } |
| } |
| return smsSession; |
| } |
| |
| /** |
| * Finish the call session and move it into the completed session |
| * |
| * @param inProgressCallSession The in progress call session |
| */ |
| private synchronized void finishCallSession(InProgressCallSession inProgressCallSession) { |
| TelephonyCallSession callSession = new TelephonyCallSession(); |
| callSession.events = new TelephonyCallSession.Event[inProgressCallSession.events.size()]; |
| inProgressCallSession.events.toArray(callSession.events); |
| callSession.startTimeMinutes = inProgressCallSession.startSystemTimeMin; |
| callSession.phoneId = inProgressCallSession.phoneId; |
| callSession.eventsDropped = inProgressCallSession.isEventsDropped(); |
| if (mCompletedCallSessions.size() >= MAX_COMPLETED_CALL_SESSIONS) { |
| mCompletedCallSessions.removeFirst(); |
| } |
| mCompletedCallSessions.add(callSession); |
| mInProgressCallSessions.remove(inProgressCallSession.phoneId); |
| if (VDBG) Rlog.v(TAG, "Call session finished"); |
| } |
| |
| /** |
| * Finish the SMS session and move it into the completed session |
| * |
| * @param inProgressSmsSession The in progress SMS session |
| */ |
| private synchronized void finishSmsSessionIfNeeded(InProgressSmsSession inProgressSmsSession) { |
| if (inProgressSmsSession.getNumExpectedResponses() == 0) { |
| SmsSession smsSession = new SmsSession(); |
| smsSession.events = new SmsSession.Event[inProgressSmsSession.events.size()]; |
| inProgressSmsSession.events.toArray(smsSession.events); |
| smsSession.startTimeMinutes = inProgressSmsSession.startSystemTimeMin; |
| smsSession.phoneId = inProgressSmsSession.phoneId; |
| smsSession.eventsDropped = inProgressSmsSession.isEventsDropped(); |
| if (mCompletedSmsSessions.size() >= MAX_COMPLETED_SMS_SESSIONS) { |
| mCompletedSmsSessions.removeFirst(); |
| } |
| mCompletedSmsSessions.add(smsSession); |
| mInProgressSmsSessions.remove(inProgressSmsSession.phoneId); |
| if (VDBG) Rlog.v(TAG, "SMS session finished"); |
| } |
| } |
| |
| /** |
| * Add telephony event into the queue |
| * |
| * @param event Telephony event |
| */ |
| private synchronized void addTelephonyEvent(TelephonyEvent event) { |
| if (mTelephonyEvents.size() >= MAX_TELEPHONY_EVENTS) { |
| mTelephonyEvents.removeFirst(); |
| mTelephonyEventsDropped = true; |
| } |
| mTelephonyEvents.add(event); |
| } |
| |
| /** |
| * Write service changed event |
| * |
| * @param phoneId Phone id |
| * @param serviceState Service state |
| */ |
| public synchronized void writeServiceStateChanged(int phoneId, ServiceState serviceState) { |
| |
| TelephonyEvent event = new TelephonyEventBuilder(phoneId) |
| .setServiceState(toServiceStateProto(serviceState)).build(); |
| |
| // If service state doesn't change, we don't log the event. |
| if (mLastServiceState.get(phoneId) != null && |
| Arrays.equals(TelephonyServiceState.toByteArray(mLastServiceState.get(phoneId)), |
| TelephonyServiceState.toByteArray(event.serviceState))) { |
| return; |
| } |
| |
| mLastServiceState.put(phoneId, event.serviceState); |
| addTelephonyEvent(event); |
| |
| annotateInProgressCallSession(event.timestampMillis, phoneId, |
| new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) |
| .setServiceState(event.serviceState)); |
| annotateInProgressSmsSession(event.timestampMillis, phoneId, |
| new SmsSessionEventBuilder( |
| SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED) |
| .setServiceState(event.serviceState)); |
| } |
| |
| /** |
| * Write data stall event |
| * |
| * @param phoneId Phone id |
| * @param recoveryAction Data stall recovery action |
| */ |
| public void writeDataStallEvent(int phoneId, int recoveryAction) { |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId) |
| .setDataStallRecoveryAction(recoveryAction).build()); |
| } |
| |
| /** |
| * Write IMS feature settings changed event |
| * |
| * @param phoneId Phone id |
| * @param feature IMS feature |
| * @param network The IMS network type |
| * @param value The settings. 0 indicates disabled, otherwise enabled. |
| * @param status IMS operation status. See OperationStatusConstants for details. |
| */ |
| public void writeImsSetFeatureValue(int phoneId, int feature, int network, int value, |
| int status) { |
| TelephonySettings s = new TelephonySettings(); |
| switch (feature) { |
| case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE: |
| s.isEnhanced4GLteModeEnabled = (value != 0); |
| break; |
| case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI: |
| s.isWifiCallingEnabled = (value != 0); |
| break; |
| case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE: |
| s.isVtOverLteEnabled = (value != 0); |
| break; |
| case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI: |
| s.isVtOverWifiEnabled = (value != 0); |
| break; |
| } |
| |
| // If the settings don't change, we don't log the event. |
| if (mLastSettings.get(phoneId) != null && |
| Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)), |
| TelephonySettings.toByteArray(s))) { |
| return; |
| } |
| |
| mLastSettings.put(phoneId, s); |
| |
| TelephonyEvent event = new TelephonyEventBuilder(phoneId).setSettings(s).build(); |
| addTelephonyEvent(event); |
| |
| annotateInProgressCallSession(event.timestampMillis, phoneId, |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.SETTINGS_CHANGED) |
| .setSettings(s)); |
| annotateInProgressSmsSession(event.timestampMillis, phoneId, |
| new SmsSessionEventBuilder(SmsSession.Event.Type.SETTINGS_CHANGED) |
| .setSettings(s)); |
| } |
| |
| /** |
| * Write the preferred network settings changed event |
| * |
| * @param phoneId Phone id |
| * @param networkType The preferred network |
| */ |
| public void writeSetPreferredNetworkType(int phoneId, int networkType) { |
| TelephonySettings s = new TelephonySettings(); |
| s.preferredNetworkMode = networkType + 1; |
| |
| // If the settings don't change, we don't log the event. |
| if (mLastSettings.get(phoneId) != null && |
| Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)), |
| TelephonySettings.toByteArray(s))) { |
| return; |
| } |
| |
| mLastSettings.put(phoneId, s); |
| |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSettings(s).build()); |
| } |
| |
| /** |
| * Write the IMS connection state changed event |
| * |
| * @param phoneId Phone id |
| * @param state IMS connection state |
| * @param reasonInfo The reason info. Only used for disconnected state. |
| */ |
| public synchronized void writeOnImsConnectionState(int phoneId, int state, |
| ImsReasonInfo reasonInfo) { |
| ImsConnectionState imsState = new ImsConnectionState(); |
| imsState.state = state; |
| |
| if (reasonInfo != null) { |
| TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo(); |
| |
| ri.reasonCode = reasonInfo.getCode(); |
| ri.extraCode = reasonInfo.getExtraCode(); |
| String extraMessage = reasonInfo.getExtraMessage(); |
| if (extraMessage != null) { |
| ri.extraMessage = extraMessage; |
| } |
| |
| imsState.reasonInfo = ri; |
| } |
| |
| // If the connection state does not change, do not log it. |
| if (mLastImsConnectionState.get(phoneId) != null && |
| Arrays.equals(ImsConnectionState.toByteArray(mLastImsConnectionState.get(phoneId)), |
| ImsConnectionState.toByteArray(imsState))) { |
| return; |
| } |
| |
| mLastImsConnectionState.put(phoneId, imsState); |
| |
| TelephonyEvent event = new TelephonyEventBuilder(phoneId) |
| .setImsConnectionState(imsState).build(); |
| addTelephonyEvent(event); |
| |
| annotateInProgressCallSession(event.timestampMillis, phoneId, |
| new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED) |
| .setImsConnectionState(event.imsConnectionState)); |
| annotateInProgressSmsSession(event.timestampMillis, phoneId, |
| new SmsSessionEventBuilder( |
| SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED) |
| .setImsConnectionState(event.imsConnectionState)); |
| } |
| |
| /** |
| * Write the IMS capabilities changed event |
| * |
| * @param phoneId Phone id |
| * @param capabilities IMS capabilities array |
| */ |
| public synchronized void writeOnImsCapabilities(int phoneId, boolean[] capabilities) { |
| ImsCapabilities cap = new ImsCapabilities(); |
| |
| cap.voiceOverLte = capabilities[0]; |
| cap.videoOverLte = capabilities[1]; |
| cap.voiceOverWifi = capabilities[2]; |
| cap.videoOverWifi = capabilities[3]; |
| cap.utOverLte = capabilities[4]; |
| cap.utOverWifi = capabilities[5]; |
| |
| TelephonyEvent event = new TelephonyEventBuilder(phoneId).setImsCapabilities(cap).build(); |
| |
| // If the capabilities don't change, we don't log the event. |
| if (mLastImsCapabilities.get(phoneId) != null && |
| Arrays.equals(ImsCapabilities.toByteArray(mLastImsCapabilities.get(phoneId)), |
| ImsCapabilities.toByteArray(cap))) { |
| return; |
| } |
| |
| mLastImsCapabilities.put(phoneId, cap); |
| addTelephonyEvent(event); |
| |
| annotateInProgressCallSession(event.timestampMillis, phoneId, |
| new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED) |
| .setImsCapabilities(event.imsCapabilities)); |
| annotateInProgressSmsSession(event.timestampMillis, phoneId, |
| new SmsSessionEventBuilder( |
| SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED) |
| .setImsCapabilities(event.imsCapabilities)); |
| } |
| |
| /** |
| * Convert PDP type into the enumeration |
| * |
| * @param type PDP type |
| * @return The proto defined enumeration |
| */ |
| private int toPdpType(String type) { |
| switch (type) { |
| case "IP": |
| return PDP_TYPE_IP; |
| case "IPV6": |
| return PDP_TYPE_IPV6; |
| case "IPV4V6": |
| return PDP_TYPE_IPV4V6; |
| case "PPP": |
| return PDP_TYPE_PPP; |
| } |
| Rlog.e(TAG, "Unknown type: " + type); |
| return PDP_UNKNOWN; |
| } |
| |
| /** |
| * Write setup data call event |
| * |
| * @param phoneId Phone id |
| * @param rilSerial RIL request serial number |
| * @param radioTechnology The data call RAT |
| * @param profile Data profile |
| * @param apn APN in string |
| * @param authType Authentication type |
| * @param protocol Data connection protocol |
| */ |
| public void writeRilSetupDataCall(int phoneId, int rilSerial, int radioTechnology, int profile, |
| String apn, int authType, String protocol) { |
| |
| RilSetupDataCall setupDataCall = new RilSetupDataCall(); |
| setupDataCall.rat = radioTechnology; |
| setupDataCall.dataProfile = profile + 1; // off by 1 between proto and RIL constants. |
| if (apn != null) { |
| setupDataCall.apn = apn; |
| } |
| if (protocol != null) { |
| setupDataCall.type = toPdpType(protocol); |
| } |
| |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSetupDataCall( |
| setupDataCall).build()); |
| } |
| |
| /** |
| * Write data call deactivate event |
| * |
| * @param phoneId Phone id |
| * @param rilSerial RIL request serial number |
| * @param cid call id |
| * @param reason Deactivate reason |
| */ |
| public void writeRilDeactivateDataCall(int phoneId, int rilSerial, int cid, int reason) { |
| |
| RilDeactivateDataCall deactivateDataCall = new RilDeactivateDataCall(); |
| deactivateDataCall.cid = cid; |
| deactivateDataCall.reason = reason + 1; |
| |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDeactivateDataCall( |
| deactivateDataCall).build()); |
| } |
| |
| /** |
| * Write get data call list event |
| * |
| * @param phoneId Phone id |
| * @param dcsList Data call list |
| */ |
| public void writeRilDataCallList(int phoneId, ArrayList<DataCallResponse> dcsList) { |
| |
| RilDataCall[] dataCalls = new RilDataCall[dcsList.size()]; |
| |
| for (int i = 0; i < dcsList.size(); i++) { |
| dataCalls[i] = new RilDataCall(); |
| dataCalls[i].cid = dcsList.get(i).cid; |
| if (!TextUtils.isEmpty(dcsList.get(i).ifname)) { |
| dataCalls[i].iframe = dcsList.get(i).ifname; |
| } |
| if (!TextUtils.isEmpty(dcsList.get(i).type)) { |
| dataCalls[i].type = toPdpType(dcsList.get(i).type); |
| } |
| } |
| |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDataCalls(dataCalls).build()); |
| } |
| |
| /** |
| * Write CS call list event |
| * |
| * @param phoneId Phone id |
| * @param connections Array of GsmCdmaConnection objects |
| */ |
| public void writeRilCallList(int phoneId, ArrayList<GsmCdmaConnection> connections) { |
| if (VDBG) { |
| Rlog.v(TAG, "Logging CallList Changed Connections Size = " + connections.size()); |
| } |
| InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "writeRilCallList: Call session is missing"); |
| } else { |
| RilCall[] calls = convertConnectionsToRilCalls(connections); |
| callSession.addEvent( |
| new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED) |
| .setRilCalls(calls) |
| ); |
| if (VDBG) Rlog.v(TAG, "Logged Call list changed"); |
| if (callSession.isPhoneIdle() && disconnectReasonsKnown(calls)) { |
| finishCallSession(callSession); |
| } |
| } |
| } |
| |
| private boolean disconnectReasonsKnown(RilCall[] calls) { |
| for (RilCall call : calls) { |
| if (call.callEndReason == 0) return false; |
| } |
| return true; |
| } |
| |
| private RilCall[] convertConnectionsToRilCalls(ArrayList<GsmCdmaConnection> mConnections) { |
| RilCall[] calls = new RilCall[mConnections.size()]; |
| for (int i = 0; i < mConnections.size(); i++) { |
| calls[i] = new RilCall(); |
| calls[i].index = i; |
| convertConnectionToRilCall(mConnections.get(i), calls[i]); |
| } |
| return calls; |
| } |
| |
| private void convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call) { |
| if (conn.isIncoming()) { |
| call.type = Type.MT; |
| } else { |
| call.type = Type.MO; |
| } |
| switch (conn.getState()) { |
| case IDLE: |
| call.state = CallState.CALL_IDLE; |
| break; |
| case ACTIVE: |
| call.state = CallState.CALL_ACTIVE; |
| break; |
| case HOLDING: |
| call.state = CallState.CALL_HOLDING; |
| break; |
| case DIALING: |
| call.state = CallState.CALL_DIALING; |
| break; |
| case ALERTING: |
| call.state = CallState.CALL_ALERTING; |
| break; |
| case INCOMING: |
| call.state = CallState.CALL_INCOMING; |
| break; |
| case WAITING: |
| call.state = CallState.CALL_WAITING; |
| break; |
| case DISCONNECTED: |
| call.state = CallState.CALL_DISCONNECTED; |
| break; |
| case DISCONNECTING: |
| call.state = CallState.CALL_DISCONNECTING; |
| break; |
| default: |
| call.state = CallState.CALL_UNKNOWN; |
| break; |
| } |
| call.callEndReason = conn.getDisconnectCause(); |
| call.isMultiparty = conn.isMultiparty(); |
| } |
| |
| /** |
| * Write dial event |
| * |
| * @param phoneId Phone id |
| * @param conn Connection object created to track this call |
| * @param clirMode CLIR (Calling Line Identification Restriction) mode |
| * @param uusInfo User-to-User signaling Info |
| */ |
| public void writeRilDial(int phoneId, GsmCdmaConnection conn, int clirMode, UUSInfo uusInfo) { |
| |
| InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId); |
| if (VDBG) Rlog.v(TAG, "Logging Dial Connection = " + conn); |
| if (callSession == null) { |
| Rlog.e(TAG, "writeRilDial: Call session is missing"); |
| } else { |
| RilCall[] calls = new RilCall[1]; |
| calls[0] = new RilCall(); |
| calls[0].index = -1; |
| convertConnectionToRilCall(conn, calls[0]); |
| callSession.addEvent(callSession.startElapsedTimeMs, |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST) |
| .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL) |
| .setRilCalls(calls)); |
| if (VDBG) Rlog.v(TAG, "Logged Dial event"); |
| } |
| } |
| |
| /** |
| * Write incoming call event |
| * |
| * @param phoneId Phone id |
| * @param response Unused today |
| */ |
| public void writeRilCallRing(int phoneId, char[] response) { |
| InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId); |
| |
| callSession.addEvent(callSession.startElapsedTimeMs, |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_CALL_RING)); |
| } |
| |
| /** |
| * Write call hangup event |
| * |
| * @param phoneId Phone id |
| * @param conn Connection object associated with the call that is being hung-up |
| * @param callId Call id |
| */ |
| public void writeRilHangup(int phoneId, GsmCdmaConnection conn, int callId) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "writeRilHangup: Call session is missing"); |
| } else { |
| RilCall[] calls = new RilCall[1]; |
| calls[0] = new RilCall(); |
| calls[0].index = callId; |
| convertConnectionToRilCall(conn, calls[0]); |
| callSession.addEvent( |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST) |
| .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP) |
| .setRilCalls(calls)); |
| if (VDBG) Rlog.v(TAG, "Logged Hangup event"); |
| } |
| } |
| |
| /** |
| * Write call answer event |
| * |
| * @param phoneId Phone id |
| * @param rilSerial RIL request serial number |
| */ |
| public void writeRilAnswer(int phoneId, int rilSerial) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "writeRilAnswer: Call session is missing"); |
| } else { |
| callSession.addEvent( |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST) |
| .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_ANSWER) |
| .setRilRequestId(rilSerial)); |
| } |
| } |
| |
| /** |
| * Write IMS call SRVCC event |
| * |
| * @param phoneId Phone id |
| * @param rilSrvccState SRVCC state |
| */ |
| public void writeRilSrvcc(int phoneId, int rilSrvccState) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "writeRilSrvcc: Call session is missing"); |
| } else { |
| callSession.addEvent( |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_CALL_SRVCC) |
| .setSrvccState(rilSrvccState + 1)); |
| } |
| } |
| |
| /** |
| * Convert RIL request into proto defined RIL request |
| * |
| * @param r RIL request |
| * @return RIL request defined in call session proto |
| */ |
| private int toCallSessionRilRequest(int r) { |
| switch (r) { |
| case RILConstants.RIL_REQUEST_DIAL: |
| return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL; |
| |
| case RILConstants.RIL_REQUEST_ANSWER: |
| return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_ANSWER; |
| |
| case RILConstants.RIL_REQUEST_HANGUP: |
| case RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: |
| case RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: |
| return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP; |
| |
| case RILConstants.RIL_REQUEST_SET_CALL_WAITING: |
| return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_SET_CALL_WAITING; |
| |
| case RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: |
| return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE; |
| |
| case RILConstants.RIL_REQUEST_CDMA_FLASH: |
| return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_CDMA_FLASH; |
| |
| case RILConstants.RIL_REQUEST_CONFERENCE: |
| return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_CONFERENCE; |
| } |
| Rlog.e(TAG, "Unknown RIL request: " + r); |
| return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_UNKNOWN; |
| } |
| |
| /** |
| * Write setup data call response event |
| * |
| * @param phoneId Phone id |
| * @param rilSerial RIL request serial number |
| * @param rilError RIL error |
| * @param rilRequest RIL request |
| * @param response Data call response |
| */ |
| private void writeOnSetupDataCallResponse(int phoneId, int rilSerial, int rilError, |
| int rilRequest, DataCallResponse response) { |
| |
| RilSetupDataCallResponse setupDataCallResponse = new RilSetupDataCallResponse(); |
| RilDataCall dataCall = new RilDataCall(); |
| |
| if (response != null) { |
| setupDataCallResponse.status = |
| (response.status == 0 ? RilDataCallFailCause.PDP_FAIL_NONE : response.status); |
| setupDataCallResponse.suggestedRetryTimeMillis = response.suggestedRetryTime; |
| |
| dataCall.cid = response.cid; |
| if (!TextUtils.isEmpty(response.type)) { |
| dataCall.type = toPdpType(response.type); |
| } |
| |
| if (!TextUtils.isEmpty(response.ifname)) { |
| dataCall.iframe = response.ifname; |
| } |
| } |
| setupDataCallResponse.call = dataCall; |
| |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId) |
| .setSetupDataCallResponse(setupDataCallResponse).build()); |
| } |
| |
| /** |
| * Write call related solicited response event |
| * |
| * @param phoneId Phone id |
| * @param rilSerial RIL request serial number |
| * @param rilError RIL error |
| * @param rilRequest RIL request |
| */ |
| private void writeOnCallSolicitedResponse(int phoneId, int rilSerial, int rilError, |
| int rilRequest) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "writeOnCallSolicitedResponse: Call session is missing"); |
| } else { |
| callSession.addEvent(new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.RIL_RESPONSE) |
| .setRilRequest(toCallSessionRilRequest(rilRequest)) |
| .setRilRequestId(rilSerial) |
| .setRilError(rilError + 1)); |
| } |
| } |
| |
| /** |
| * Write SMS related solicited response event |
| * |
| * @param phoneId Phone id |
| * @param rilSerial RIL request serial number |
| * @param rilError RIL error |
| * @param response SMS response |
| */ |
| private synchronized void writeOnSmsSolicitedResponse(int phoneId, int rilSerial, int rilError, |
| SmsResponse response) { |
| |
| InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId); |
| if (smsSession == null) { |
| Rlog.e(TAG, "SMS session is missing"); |
| } else { |
| |
| int errorCode = 0; |
| if (response != null) { |
| errorCode = response.mErrorCode; |
| } |
| |
| smsSession.addEvent(new SmsSessionEventBuilder( |
| SmsSession.Event.Type.SMS_SEND_RESULT) |
| .setErrorCode(errorCode) |
| .setRilErrno(rilError + 1) |
| .setRilRequestId(rilSerial) |
| ); |
| |
| smsSession.decreaseExpectedResponse(); |
| finishSmsSessionIfNeeded(smsSession); |
| } |
| } |
| |
| /** |
| * Write deactivate data call response event |
| * |
| * @param phoneId Phone id |
| * @param rilError RIL error |
| */ |
| private void writeOnDeactivateDataCallResponse(int phoneId, int rilError) { |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId) |
| .setDeactivateDataCallResponse(rilError + 1).build()); |
| } |
| |
| /** |
| * Write RIL solicited response event |
| * |
| * @param phoneId Phone id |
| * @param rilSerial RIL request serial number |
| * @param rilError RIL error |
| * @param rilRequest RIL request |
| * @param ret The returned RIL response |
| */ |
| public void writeOnRilSolicitedResponse(int phoneId, int rilSerial, int rilError, |
| int rilRequest, Object ret) { |
| switch (rilRequest) { |
| case RIL_REQUEST_SETUP_DATA_CALL: |
| DataCallResponse dataCall = (DataCallResponse) ret; |
| writeOnSetupDataCallResponse(phoneId, rilSerial, rilError, rilRequest, dataCall); |
| break; |
| case RIL_REQUEST_DEACTIVATE_DATA_CALL: |
| writeOnDeactivateDataCallResponse(phoneId, rilError); |
| break; |
| case RIL_REQUEST_HANGUP: |
| case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: |
| case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: |
| case RIL_REQUEST_DIAL: |
| case RIL_REQUEST_ANSWER: |
| writeOnCallSolicitedResponse(phoneId, rilSerial, rilError, rilRequest); |
| break; |
| case RIL_REQUEST_SEND_SMS: |
| case RIL_REQUEST_SEND_SMS_EXPECT_MORE: |
| case RIL_REQUEST_CDMA_SEND_SMS: |
| case RIL_REQUEST_IMS_SEND_SMS: |
| SmsResponse smsResponse = (SmsResponse) ret; |
| writeOnSmsSolicitedResponse(phoneId, rilSerial, rilError, smsResponse); |
| break; |
| } |
| } |
| |
| /** |
| * Write phone state changed event |
| * |
| * @param phoneId Phone id |
| * @param phoneState Phone state. See PhoneConstants.State for the details. |
| */ |
| public void writePhoneState(int phoneId, PhoneConstants.State phoneState) { |
| int state; |
| switch (phoneState) { |
| case IDLE: |
| state = TelephonyCallSession.Event.PhoneState.STATE_IDLE; |
| break; |
| case RINGING: |
| state = TelephonyCallSession.Event.PhoneState.STATE_RINGING; |
| break; |
| case OFFHOOK: |
| state = TelephonyCallSession.Event.PhoneState.STATE_OFFHOOK; |
| break; |
| default: |
| state = TelephonyCallSession.Event.PhoneState.STATE_UNKNOWN; |
| break; |
| } |
| |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "writePhoneState: Call session is missing"); |
| } else { |
| // For CS Calls Finish the Call Session after Receiving the Last Call Fail Cause |
| // For IMS calls we receive the Disconnect Cause along with Call End event. |
| // So we can finish the call session here. |
| callSession.setLastKnownPhoneState(state); |
| if ((state == TelephonyCallSession.Event.PhoneState.STATE_IDLE) |
| && (!callSession.containsCsCalls())) { |
| finishCallSession(callSession); |
| } |
| callSession.addEvent(new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED) |
| .setPhoneState(state)); |
| } |
| } |
| |
| /** |
| * Extracts the call ID from an ImsSession. |
| * |
| * @param session The session. |
| * @return The call ID for the session, or -1 if none was found. |
| */ |
| private int getCallId(ImsCallSession session) { |
| if (session == null) { |
| return -1; |
| } |
| |
| try { |
| return Integer.parseInt(session.getCallId()); |
| } catch (NumberFormatException nfe) { |
| return -1; |
| } |
| } |
| |
| /** |
| * Write IMS call state changed event |
| * |
| * @param phoneId Phone id |
| * @param session IMS call session |
| * @param callState IMS call state |
| */ |
| public void writeImsCallState(int phoneId, ImsCallSession session, |
| ImsPhoneCall.State callState) { |
| int state; |
| switch (callState) { |
| case IDLE: |
| state = TelephonyCallSession.Event.CallState.CALL_IDLE; break; |
| case ACTIVE: |
| state = TelephonyCallSession.Event.CallState.CALL_ACTIVE; break; |
| case HOLDING: |
| state = TelephonyCallSession.Event.CallState.CALL_HOLDING; break; |
| case DIALING: |
| state = TelephonyCallSession.Event.CallState.CALL_DIALING; break; |
| case ALERTING: |
| state = TelephonyCallSession.Event.CallState.CALL_ALERTING; break; |
| case INCOMING: |
| state = TelephonyCallSession.Event.CallState.CALL_INCOMING; break; |
| case WAITING: |
| state = TelephonyCallSession.Event.CallState.CALL_WAITING; break; |
| case DISCONNECTED: |
| state = TelephonyCallSession.Event.CallState.CALL_DISCONNECTED; break; |
| case DISCONNECTING: |
| state = TelephonyCallSession.Event.CallState.CALL_DISCONNECTING; break; |
| default: |
| state = TelephonyCallSession.Event.CallState.CALL_UNKNOWN; break; |
| } |
| |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "Call session is missing"); |
| } else { |
| callSession.addEvent(new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.IMS_CALL_STATE_CHANGED) |
| .setCallIndex(getCallId(session)) |
| .setCallState(state)); |
| } |
| } |
| |
| /** |
| * Write IMS call start event |
| * |
| * @param phoneId Phone id |
| * @param session IMS call session |
| */ |
| public void writeOnImsCallStart(int phoneId, ImsCallSession session) { |
| InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId); |
| |
| callSession.addEvent( |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_COMMAND) |
| .setCallIndex(getCallId(session)) |
| .setImsCommand(TelephonyCallSession.Event.ImsCommand.IMS_CMD_START)); |
| } |
| |
| /** |
| * Write IMS incoming call event |
| * |
| * @param phoneId Phone id |
| * @param session IMS call session |
| */ |
| public void writeOnImsCallReceive(int phoneId, ImsCallSession session) { |
| InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId); |
| |
| callSession.addEvent( |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_CALL_RECEIVE) |
| .setCallIndex(getCallId(session))); |
| } |
| |
| /** |
| * Write IMS command event |
| * |
| * @param phoneId Phone id |
| * @param session IMS call session |
| * @param command IMS command |
| */ |
| public void writeOnImsCommand(int phoneId, ImsCallSession session, int command) { |
| |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "Call session is missing"); |
| } else { |
| callSession.addEvent( |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_COMMAND) |
| .setCallIndex(getCallId(session)) |
| .setImsCommand(command)); |
| } |
| } |
| |
| /** |
| * Convert IMS reason info into proto |
| * |
| * @param reasonInfo IMS reason info |
| * @return Converted proto |
| */ |
| private TelephonyProto.ImsReasonInfo toImsReasonInfoProto(ImsReasonInfo reasonInfo) { |
| TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo(); |
| if (reasonInfo != null) { |
| ri.reasonCode = reasonInfo.getCode(); |
| ri.extraCode = reasonInfo.getExtraCode(); |
| String extraMessage = reasonInfo.getExtraMessage(); |
| if (extraMessage != null) { |
| ri.extraMessage = extraMessage; |
| } |
| } |
| return ri; |
| } |
| |
| /** |
| * Write IMS call end event |
| * |
| * @param phoneId Phone id |
| * @param session IMS call session |
| * @param reasonInfo Call end reason |
| */ |
| public void writeOnImsCallTerminated(int phoneId, ImsCallSession session, |
| ImsReasonInfo reasonInfo) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "Call session is missing"); |
| } else { |
| callSession.addEvent( |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED) |
| .setCallIndex(getCallId(session)) |
| .setImsReasonInfo(toImsReasonInfoProto(reasonInfo))); |
| } |
| } |
| |
| /** |
| * Write IMS call hangover event |
| * |
| * @param phoneId Phone id |
| * @param eventType hangover type |
| * @param session IMS call session |
| * @param srcAccessTech Hangover starting RAT |
| * @param targetAccessTech Hangover destination RAT |
| * @param reasonInfo Hangover reason |
| */ |
| public void writeOnImsCallHandoverEvent(int phoneId, int eventType, ImsCallSession session, |
| int srcAccessTech, int targetAccessTech, |
| ImsReasonInfo reasonInfo) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "Call session is missing"); |
| } else { |
| callSession.addEvent( |
| new CallSessionEventBuilder(eventType) |
| .setCallIndex(getCallId(session)) |
| .setSrcAccessTech(srcAccessTech) |
| .setTargetAccessTech(targetAccessTech) |
| .setImsReasonInfo(toImsReasonInfoProto(reasonInfo))); |
| } |
| } |
| |
| /** |
| * Write Send SMS event |
| * |
| * @param phoneId Phone id |
| * @param rilSerial RIL request serial number |
| * @param tech SMS RAT |
| * @param format SMS format. Either 3GPP or 3GPP2. |
| */ |
| public synchronized void writeRilSendSms(int phoneId, int rilSerial, int tech, int format) { |
| InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId); |
| |
| smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND) |
| .setTech(tech) |
| .setRilRequestId(rilSerial) |
| .setFormat(format) |
| ); |
| |
| smsSession.increaseExpectedResponse(); |
| } |
| |
| /** |
| * Write incoming SMS event |
| * |
| * @param phoneId Phone id |
| * @param tech SMS RAT |
| * @param format SMS format. Either 3GPP or 3GPP2. |
| */ |
| public synchronized void writeRilNewSms(int phoneId, int tech, int format) { |
| InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId); |
| |
| smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED) |
| .setTech(tech) |
| .setFormat(format) |
| ); |
| |
| finishSmsSessionIfNeeded(smsSession); |
| } |
| |
| /** |
| * Write incoming Broadcast SMS event |
| * |
| * @param phoneId Phone id |
| * @param format CB msg format |
| * @param priority CB msg priority |
| * @param isCMAS true if msg is CMAS |
| * @param isETWS true if msg is ETWS |
| * @param serviceCategory Service category of CB msg |
| */ |
| public synchronized void writeNewCBSms(int phoneId, int format, int priority, boolean isCMAS, |
| boolean isETWS, int serviceCategory) { |
| InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId); |
| |
| int type; |
| if (isCMAS) { |
| type = SmsSession.Event.CBMessageType.CMAS; |
| } else if (isETWS) { |
| type = SmsSession.Event.CBMessageType.ETWS; |
| } else { |
| type = SmsSession.Event.CBMessageType.OTHER; |
| } |
| |
| SmsSession.Event.CBMessage cbm = new SmsSession.Event.CBMessage(); |
| cbm.msgFormat = format; |
| cbm.msgPriority = priority + 1; |
| cbm.msgType = type; |
| cbm.serviceCategory = serviceCategory; |
| |
| smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.CB_SMS_RECEIVED) |
| .setCellBroadcastMessage(cbm) |
| ); |
| |
| finishSmsSessionIfNeeded(smsSession); |
| } |
| |
| /** |
| * Write NITZ event |
| * |
| * @param phoneId Phone id |
| * @param timestamp NITZ time in milliseconds |
| */ |
| public void writeNITZEvent(int phoneId, long timestamp) { |
| TelephonyEvent event = new TelephonyEventBuilder(phoneId).setNITZ(timestamp).build(); |
| addTelephonyEvent(event); |
| |
| annotateInProgressCallSession(event.timestampMillis, phoneId, |
| new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.NITZ_TIME) |
| .setNITZ(timestamp)); |
| } |
| |
| /** |
| * Write Modem Restart event |
| * |
| * @param phoneId Phone id |
| * @param reason Reason for the modem reset. |
| */ |
| public void writeModemRestartEvent(int phoneId, String reason) { |
| final ModemRestart modemRestart = new ModemRestart(); |
| String basebandVersion = Build.getRadioVersion(); |
| if (basebandVersion != null) modemRestart.basebandVersion = basebandVersion; |
| if (reason != null) modemRestart.reason = reason; |
| TelephonyEvent event = new TelephonyEventBuilder(phoneId).setModemRestart( |
| modemRestart).build(); |
| addTelephonyEvent(event); |
| } |
| |
| //TODO: Expand the proto in the future |
| public void writeOnImsCallProgressing(int phoneId, ImsCallSession session) {} |
| public void writeOnImsCallStarted(int phoneId, ImsCallSession session) {} |
| public void writeOnImsCallStartFailed(int phoneId, ImsCallSession session, |
| ImsReasonInfo reasonInfo) {} |
| public void writeOnImsCallHeld(int phoneId, ImsCallSession session) {} |
| public void writeOnImsCallHoldReceived(int phoneId, ImsCallSession session) {} |
| public void writeOnImsCallHoldFailed(int phoneId, ImsCallSession session, |
| ImsReasonInfo reasonInfo) {} |
| public void writeOnImsCallResumed(int phoneId, ImsCallSession session) {} |
| public void writeOnImsCallResumeReceived(int phoneId, ImsCallSession session) {} |
| public void writeOnImsCallResumeFailed(int phoneId, ImsCallSession session, |
| ImsReasonInfo reasonInfo) {} |
| public void writeOnRilTimeoutResponse(int phoneId, int rilSerial, int rilRequest) {} |
| } |