| /* |
| * 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_NON_IP; |
| import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_PPP; |
| import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_UNSTRUCTURED; |
| import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_UNKNOWN; |
| |
| import android.content.Context; |
| import android.net.NetworkCapabilities; |
| import android.os.BatteryStatsManager; |
| import android.os.Build; |
| import android.os.SystemClock; |
| import android.os.SystemProperties; |
| import android.provider.Telephony.Sms.Intents; |
| import android.telephony.AccessNetworkConstants; |
| import android.telephony.CallQuality; |
| import android.telephony.DisconnectCause; |
| import android.telephony.NetworkRegistrationInfo; |
| import android.telephony.ServiceState; |
| import android.telephony.SmsManager; |
| import android.telephony.SmsMessage; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyHistogram; |
| import android.telephony.TelephonyManager; |
| import android.telephony.data.DataCallResponse; |
| import android.telephony.data.DataService; |
| import android.telephony.emergency.EmergencyNumber; |
| import android.telephony.ims.ImsCallProfile; |
| import android.telephony.ims.ImsCallSession; |
| import android.telephony.ims.ImsReasonInfo; |
| import android.telephony.ims.ImsStreamMediaProfile; |
| import android.telephony.ims.feature.MmTelFeature; |
| import android.telephony.ims.stub.ImsRegistrationImplBase; |
| import android.telephony.ims.stub.ImsSmsImplBase; |
| import android.text.TextUtils; |
| import android.util.Base64; |
| import android.util.SparseArray; |
| |
| import com.android.internal.telephony.CarrierResolver; |
| import com.android.internal.telephony.DriverCall; |
| 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.emergency.EmergencyNumberTracker; |
| import com.android.internal.telephony.imsphone.ImsPhoneCall; |
| import com.android.internal.telephony.nano.TelephonyProto; |
| import com.android.internal.telephony.nano.TelephonyProto.ActiveSubscriptionInfo; |
| import com.android.internal.telephony.nano.TelephonyProto.EmergencyNumberInfo; |
| import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities; |
| import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState; |
| import com.android.internal.telephony.nano.TelephonyProto.ModemPowerStats; |
| import com.android.internal.telephony.nano.TelephonyProto.RilDataCall; |
| import com.android.internal.telephony.nano.TelephonyProto.SimState; |
| 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.CarrierIdMatching; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatchingResult; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierKeyChange; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.DataSwitch; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.NetworkCapabilitiesInfo; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.OnDemandDataSwitch; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall; |
| import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall.DeactivateReason; |
| 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.telephony.protobuf.nano.MessageNano; |
| import com.android.internal.telephony.util.TelephonyUtils; |
| import com.android.internal.util.IndentingPrintWriter; |
| import com.android.telephony.Rlog; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.text.DecimalFormat; |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Deque; |
| import java.util.List; |
| import java.util.concurrent.ThreadLocalRandom; |
| |
| /** |
| * 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<>(); |
| |
| /** |
| * Last sim state, indexed by phone id. |
| */ |
| private final SparseArray<Integer> mLastSimState = new SparseArray<>(); |
| |
| /** |
| * Last active subscription information, indexed by phone id. |
| */ |
| private final SparseArray<ActiveSubscriptionInfo> mLastActiveSubscriptionInfos = |
| new SparseArray<>(); |
| |
| /** |
| * The last modem state represent by a bitmap, the i-th bit(LSB) indicates the i-th modem |
| * state(0 - disabled, 1 - enabled). |
| * |
| * TODO: initialize the enabled modem bitmap when it's possible to get the modem state. |
| */ |
| private int mLastEnabledModemBitmap = (1 << TelephonyManager.getDefault().getPhoneCount()) - 1; |
| |
| /** |
| * Last carrier id matching. |
| */ |
| private final SparseArray<CarrierIdMatching> mLastCarrierId = new SparseArray<>(); |
| |
| /** |
| * Last NetworkCapabilitiesInfo, indexed by phone id. |
| */ |
| private final SparseArray<NetworkCapabilitiesInfo> mLastNetworkCapabilitiesInfos = |
| new SparseArray<>(); |
| |
| /** |
| * Last RilDataCall Events (indexed by cid), indexed by phone id |
| */ |
| private final SparseArray<SparseArray<RilDataCall>> mLastRilDataCallEvents = |
| 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; |
| |
| private Context mContext; |
| |
| public TelephonyMetrics() { |
| mStartSystemTimeMs = System.currentTimeMillis(); |
| mStartElapsedTimeMs = SystemClock.elapsedRealtime(); |
| } |
| |
| /** |
| * Get the singleton instance of telephony metrics. |
| * |
| * @return The instance |
| */ |
| public synchronized static TelephonyMetrics getInstance() { |
| if (sInstance == null) { |
| sInstance = new TelephonyMetrics(); |
| } |
| |
| return sInstance; |
| } |
| |
| /** |
| * Set the context for telephony metrics. |
| * |
| * @param context Context |
| * @hide |
| */ |
| public void setContext(Context context) { |
| mContext = context; |
| } |
| |
| /** |
| * 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) { |
| boolean reset = true; |
| if (args.length > 1 && "--keep".equals(args[1])) { |
| reset = false; |
| } |
| |
| switch (args[0]) { |
| case "--metrics": |
| printAllMetrics(pw); |
| break; |
| case "--metricsproto": |
| pw.println(convertProtoToBase64String(buildProto())); |
| if (reset) { |
| reset(); |
| } |
| break; |
| case "--metricsprototext": |
| pw.println(buildProto().toString()); |
| break; |
| } |
| } |
| } |
| |
| private void logv(String log) { |
| if (VDBG) { |
| Rlog.v(TAG, log); |
| } |
| } |
| |
| /** |
| * 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"; |
| case TelephonyEvent.Type.CARRIER_ID_MATCHING: |
| return "CARRIER_ID_MATCHING"; |
| case TelephonyEvent.Type.NITZ_TIME: |
| return "NITZ_TIME"; |
| case TelephonyEvent.Type.EMERGENCY_NUMBER_REPORT: |
| return "EMERGENCY_NUMBER_REPORT"; |
| case TelephonyEvent.Type.NETWORK_CAPABILITIES_CHANGED: |
| return "NETWORK_CAPABILITIES_CHANGED"; |
| 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"; |
| case TelephonyCallSession.Event.Type.AUDIO_CODEC: |
| return "AUDIO_CODEC"; |
| 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"; |
| case SmsSession.Event.Type.INCOMPLETE_SMS_RECEIVED: |
| return "INCOMPLETE_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) |
| + "(" + "Data RAT " + event.serviceState.dataRat |
| + " Voice RAT " + event.serviceState.voiceRat |
| + " Channel Number " + event.serviceState.channelNumber |
| + " NR Frequency Range " + event.serviceState.nrFrequencyRange |
| + " NR State " + event.serviceState.nrState |
| + ")"); |
| for (int i = 0; i < event.serviceState.networkRegistrationInfo.length; i++) { |
| pw.print("reg info: domain=" |
| + event.serviceState.networkRegistrationInfo[i].domain |
| + ", rat=" + event.serviceState.networkRegistrationInfo[i].rat); |
| } |
| } else { |
| pw.print(telephonyEventToString(event.type)); |
| } |
| |
| pw.println(""); |
| } |
| |
| pw.decreaseIndent(); |
| pw.println("Call sessions:"); |
| pw.increaseIndent(); |
| |
| for (TelephonyCallSession callSession : mCompletedCallSessions) { |
| pw.print("Start time in minutes: " + callSession.startTimeMinutes); |
| pw.print(", phone: " + callSession.phoneId); |
| if (callSession.eventsDropped) { |
| 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) |
| + "(" + "Data RAT " + event.serviceState.dataRat |
| + " Voice RAT " + event.serviceState.voiceRat |
| + " Channel Number " + event.serviceState.channelNumber |
| + " NR Frequency Range " + event.serviceState.nrFrequencyRange |
| + " NR State " + event.serviceState.nrState |
| + ")"); |
| } 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 |
| + " Precise Disconnect Cause " + call.preciseDisconnectCause |
| + " isMultiparty = " + call.isMultiparty); |
| } |
| pw.decreaseIndent(); |
| } else if (event.type == TelephonyCallSession.Event.Type.AUDIO_CODEC) { |
| pw.println(callSessionEventToString(event.type) |
| + "(" + event.audioCodec + ")"); |
| } 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); |
| pw.print(", phone: " + smsSession.phoneId); |
| if (smsSession.eventsDropped) { |
| pw.println(", events dropped: " + smsSession.eventsDropped); |
| } else { |
| pw.println(""); |
| } |
| pw.println("Events: "); |
| pw.increaseIndent(); |
| for (SmsSession.Event event : smsSession.events) { |
| pw.print(event.delay); |
| pw.print(" T="); |
| pw.println(smsSessionEventToString(event.type)); |
| // Only show more info for tx/rx sms |
| if (event.type == SmsSession.Event.Type.SMS_RECEIVED) { |
| pw.increaseIndent(); |
| switch (event.smsType) { |
| case SmsSession.Event.SmsType.SMS_TYPE_SMS_PP: |
| pw.println("Type: SMS-PP"); |
| break; |
| case SmsSession.Event.SmsType.SMS_TYPE_VOICEMAIL_INDICATION: |
| pw.println("Type: Voicemail indication"); |
| break; |
| case SmsSession.Event.SmsType.SMS_TYPE_ZERO: |
| pw.println("Type: zero"); |
| break; |
| case SmsSession.Event.SmsType.SMS_TYPE_WAP_PUSH: |
| pw.println("Type: WAP PUSH"); |
| break; |
| default: |
| break; |
| } |
| if (event.errorCode != SmsManager.RESULT_ERROR_NONE) { |
| pw.println("E=" + event.errorCode); |
| } |
| pw.decreaseIndent(); |
| } else if (event.type == SmsSession.Event.Type.SMS_SEND |
| || event.type == SmsSession.Event.Type.SMS_SEND_RESULT) { |
| pw.increaseIndent(); |
| pw.println("ReqId=" + event.rilRequestId); |
| pw.println("E=" + event.errorCode); |
| pw.println("RilE=" + event.error); |
| pw.println("ImsE=" + event.imsError); |
| pw.decreaseIndent(); |
| } else if (event.type == SmsSession.Event.Type.INCOMPLETE_SMS_RECEIVED) { |
| pw.increaseIndent(); |
| pw.println("Received: " + event.incompleteSms.receivedParts + "/" |
| + event.incompleteSms.totalParts); |
| pw.decreaseIndent(); |
| } |
| } |
| pw.decreaseIndent(); |
| } |
| |
| pw.decreaseIndent(); |
| pw.println("Modem power stats:"); |
| pw.increaseIndent(); |
| |
| BatteryStatsManager batteryStatsManager = mContext == null ? null : |
| (BatteryStatsManager) mContext.getSystemService(Context.BATTERY_STATS_SERVICE); |
| ModemPowerStats s = new ModemPowerMetrics(batteryStatsManager).buildProto(); |
| |
| pw.println("Power log duration (battery time) (ms): " + s.loggingDurationMs); |
| pw.println("Energy consumed by modem (mAh): " + s.energyConsumedMah); |
| pw.println("Number of packets sent (tx): " + s.numPacketsTx); |
| pw.println("Number of bytes sent (tx): " + s.numBytesTx); |
| pw.println("Number of packets received (rx): " + s.numPacketsRx); |
| pw.println("Number of bytes received (rx): " + s.numBytesRx); |
| pw.println("Amount of time kernel is active because of cellular data (ms): " |
| + s.cellularKernelActiveTimeMs); |
| pw.println("Amount of time spent in very poor rx signal level (ms): " |
| + s.timeInVeryPoorRxSignalLevelMs); |
| pw.println("Amount of time modem is in sleep (ms): " + s.sleepTimeMs); |
| pw.println("Amount of time modem is in idle (ms): " + s.idleTimeMs); |
| pw.println("Amount of time modem is in rx (ms): " + s.rxTimeMs); |
| pw.println("Amount of time modem is in tx (ms): " + Arrays.toString(s.txTimeMs)); |
| pw.println("Amount of time phone spent in various Radio Access Technologies (ms): " |
| + Arrays.toString(s.timeInRatMs)); |
| pw.println("Amount of time phone spent in various cellular " |
| + "rx signal strength levels (ms): " |
| + Arrays.toString(s.timeInRxSignalStrengthLevelMs)); |
| pw.println("Energy consumed across measured modem rails (mAh): " |
| + new DecimalFormat("#.##").format(s.monitoredRailEnergyConsumedMah)); |
| pw.decreaseIndent(); |
| pw.println("Hardware Version: " + SystemProperties.get("ro.boot.revision", "")); |
| } |
| |
| /** |
| * 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 sim state, enabled modem bitmap, active subscription info, |
| // service state, ims capabilities, ims connection states, carrier id and Data call |
| // events as the base. |
| // Sim state, modem bitmap and active subscription info events are logged before |
| // other events. |
| addTelephonyEvent(new TelephonyEventBuilder(mStartElapsedTimeMs, -1 /* phoneId */) |
| .setSimStateChange(mLastSimState).build()); |
| |
| addTelephonyEvent(new TelephonyEventBuilder(mStartElapsedTimeMs, -1 /* phoneId */) |
| .setEnabledModemBitmap(mLastEnabledModemBitmap).build()); |
| |
| for (int i = 0; i < mLastActiveSubscriptionInfos.size(); i++) { |
| final int key = mLastActiveSubscriptionInfos.keyAt(i); |
| TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) |
| .setActiveSubscriptionInfoChange(mLastActiveSubscriptionInfos.get(key)).build(); |
| addTelephonyEvent(event); |
| } |
| |
| 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); |
| } |
| |
| for (int i = 0; i < mLastCarrierId.size(); i++) { |
| final int key = mLastCarrierId.keyAt(i); |
| TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) |
| .setCarrierIdMatching(mLastCarrierId.get(key)).build(); |
| addTelephonyEvent(event); |
| } |
| |
| for (int i = 0; i < mLastNetworkCapabilitiesInfos.size(); i++) { |
| final int key = mLastNetworkCapabilitiesInfos.keyAt(i); |
| TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) |
| .setNetworkCapabilities(mLastNetworkCapabilitiesInfos.get(key)).build(); |
| addTelephonyEvent(event); |
| } |
| |
| for (int i = 0; i < mLastRilDataCallEvents.size(); i++) { |
| final int key = mLastRilDataCallEvents.keyAt(i); |
| for (int j = 0; j < mLastRilDataCallEvents.get(key).size(); j++) { |
| final int cidKey = mLastRilDataCallEvents.get(key).keyAt(j); |
| RilDataCall[] dataCalls = new RilDataCall[1]; |
| dataCalls[0] = mLastRilDataCallEvents.get(key).get(cidKey); |
| addTelephonyEvent(new TelephonyEventBuilder(mStartElapsedTimeMs, key) |
| .setDataCalls(dataCalls).build()); |
| } |
| } |
| } |
| |
| /** |
| * 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(); |
| } |
| |
| // Build modem power metrics |
| BatteryStatsManager batteryStatsManager = mContext == null ? null : |
| (BatteryStatsManager) mContext.getSystemService(Context.BATTERY_STATS_SERVICE); |
| log.modemPowerStats = new ModemPowerMetrics(batteryStatsManager).buildProto(); |
| |
| // Log the hardware revision |
| log.hardwareRevision = SystemProperties.get("ro.boot.revision", ""); |
| |
| // 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(); |
| |
| // Log the last active subscription information. |
| int phoneCount = TelephonyManager.getDefault().getPhoneCount(); |
| ActiveSubscriptionInfo[] activeSubscriptionInfo = |
| new ActiveSubscriptionInfo[phoneCount]; |
| for (int i = 0; i < mLastActiveSubscriptionInfos.size(); i++) { |
| int key = mLastActiveSubscriptionInfos.keyAt(i); |
| activeSubscriptionInfo[key] = mLastActiveSubscriptionInfos.get(key); |
| } |
| for (int i = 0; i < phoneCount; i++) { |
| if (activeSubscriptionInfo[i] == null) { |
| activeSubscriptionInfo[i] = makeInvalidSubscriptionInfo(i); |
| } |
| } |
| log.lastActiveSubscriptionInfo = activeSubscriptionInfo; |
| |
| return log; |
| } |
| |
| /** Update the sim state. */ |
| public void updateSimState(int phoneId, int simState) { |
| int state = mapSimStateToProto(simState); |
| Integer lastSimState = mLastSimState.get(phoneId); |
| if (lastSimState == null || !lastSimState.equals(state)) { |
| mLastSimState.put(phoneId, state); |
| addTelephonyEvent(new TelephonyEventBuilder().setSimStateChange(mLastSimState).build()); |
| } |
| } |
| |
| /** Update active subscription info list. */ |
| public synchronized void updateActiveSubscriptionInfoList(List<SubscriptionInfo> subInfos) { |
| List<Integer> inActivePhoneList = new ArrayList<>(); |
| for (int i = 0; i < mLastActiveSubscriptionInfos.size(); i++) { |
| inActivePhoneList.add(mLastActiveSubscriptionInfos.keyAt(i)); |
| } |
| |
| for (SubscriptionInfo info : subInfos) { |
| int phoneId = info.getSimSlotIndex(); |
| inActivePhoneList.removeIf(value -> value.equals(phoneId)); |
| ActiveSubscriptionInfo activeSubscriptionInfo = new ActiveSubscriptionInfo(); |
| activeSubscriptionInfo.slotIndex = phoneId; |
| activeSubscriptionInfo.isOpportunistic = info.isOpportunistic() ? 1 : 0; |
| activeSubscriptionInfo.carrierId = info.getCarrierId(); |
| if (!MessageNano.messageNanoEquals( |
| mLastActiveSubscriptionInfos.get(phoneId), activeSubscriptionInfo)) { |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId) |
| .setActiveSubscriptionInfoChange(activeSubscriptionInfo).build()); |
| |
| mLastActiveSubscriptionInfos.put(phoneId, activeSubscriptionInfo); |
| } |
| } |
| |
| for (int phoneId : inActivePhoneList) { |
| mLastActiveSubscriptionInfos.remove(phoneId); |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId) |
| .setActiveSubscriptionInfoChange(makeInvalidSubscriptionInfo(phoneId)).build()); |
| } |
| } |
| |
| /** Update the enabled modem bitmap. */ |
| public void updateEnabledModemBitmap(int enabledModemBitmap) { |
| if (mLastEnabledModemBitmap == enabledModemBitmap) return; |
| mLastEnabledModemBitmap = enabledModemBitmap; |
| addTelephonyEvent(new TelephonyEventBuilder() |
| .setEnabledModemBitmap(mLastEnabledModemBitmap).build()); |
| } |
| |
| private static ActiveSubscriptionInfo makeInvalidSubscriptionInfo(int phoneId) { |
| ActiveSubscriptionInfo invalidSubscriptionInfo = new ActiveSubscriptionInfo(); |
| invalidSubscriptionInfo.slotIndex = phoneId; |
| invalidSubscriptionInfo.carrierId = -1; |
| invalidSubscriptionInfo.isOpportunistic = -1; |
| return invalidSubscriptionInfo; |
| } |
| |
| /** |
| * 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)); |
| } |
| |
| /** |
| * Write the Carrier Key change event |
| * |
| * @param phoneId Phone id |
| * @param keyType type of key |
| * @param isDownloadSuccessful true if the key was successfully downloaded |
| */ |
| public void writeCarrierKeyEvent(int phoneId, int keyType, boolean isDownloadSuccessful) { |
| final CarrierKeyChange carrierKeyChange = new CarrierKeyChange(); |
| carrierKeyChange.keyType = keyType; |
| carrierKeyChange.isDownloadSuccessful = isDownloadSuccessful; |
| TelephonyEvent event = new TelephonyEventBuilder(phoneId).setCarrierKeyChange( |
| carrierKeyChange).build(); |
| addTelephonyEvent(event); |
| } |
| |
| |
| /** |
| * 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(); |
| ssProto.dataOperator = new TelephonyServiceState.TelephonyOperator(); |
| if (serviceState.getOperatorAlphaLong() != null) { |
| ssProto.voiceOperator.alphaLong = serviceState.getOperatorAlphaLong(); |
| ssProto.dataOperator.alphaLong = serviceState.getOperatorAlphaLong(); |
| } |
| |
| if (serviceState.getOperatorAlphaShort() != null) { |
| ssProto.voiceOperator.alphaShort = serviceState.getOperatorAlphaShort(); |
| ssProto.dataOperator.alphaShort = serviceState.getOperatorAlphaShort(); |
| } |
| |
| if (serviceState.getOperatorNumeric() != null) { |
| ssProto.voiceOperator.numeric = serviceState.getOperatorNumeric(); |
| ssProto.dataOperator.numeric = serviceState.getOperatorNumeric(); |
| } |
| |
| // Log PS WWAN only because CS WWAN would be exactly the same as voiceRat, and PS WLAN |
| // would be always IWLAN in the rat field. |
| // Note that we intentionally do not log reg state because it changes too frequently that |
| // will grow the proto size too much. |
| List<TelephonyServiceState.NetworkRegistrationInfo> nriList = new ArrayList<>(); |
| NetworkRegistrationInfo nri = serviceState.getNetworkRegistrationInfo( |
| NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); |
| if (nri != null) { |
| TelephonyServiceState.NetworkRegistrationInfo nriProto = |
| new TelephonyServiceState.NetworkRegistrationInfo(); |
| nriProto.domain = TelephonyServiceState.Domain.DOMAIN_PS; |
| nriProto.transport = TelephonyServiceState.Transport.TRANSPORT_WWAN; |
| nriProto.rat = ServiceState.networkTypeToRilRadioTechnology( |
| nri.getAccessNetworkTechnology()); |
| nriList.add(nriProto); |
| ssProto.networkRegistrationInfo = |
| new TelephonyServiceState.NetworkRegistrationInfo[nriList.size()]; |
| nriList.toArray(ssProto.networkRegistrationInfo); |
| } |
| |
| ssProto.voiceRat = serviceState.getRilVoiceRadioTechnology(); |
| ssProto.dataRat = serviceState.getRilDataRadioTechnology(); |
| ssProto.channelNumber = serviceState.getChannelNumber(); |
| ssProto.nrFrequencyRange = serviceState.getNrFrequencyRange(); |
| ssProto.nrState = serviceState.getNrState(); |
| 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) { |
| logv("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) { |
| logv("Starting a new sms session on phone " + phoneId); |
| smsSession = startNewSmsSession(phoneId); |
| mInProgressSmsSessions.append(phoneId, smsSession); |
| } |
| return smsSession; |
| } |
| |
| /** |
| * Create a new SMS session |
| * |
| * @param phoneId Phone id |
| * @return The SMS session |
| */ |
| private InProgressSmsSession startNewSmsSession(int phoneId) { |
| InProgressSmsSession smsSession = new InProgressSmsSession(phoneId); |
| |
| // 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); |
| logv("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 = finishSmsSession(inProgressSmsSession); |
| |
| mInProgressSmsSessions.remove(inProgressSmsSession.phoneId); |
| logv("SMS session finished"); |
| } |
| } |
| |
| private SmsSession finishSmsSession(InProgressSmsSession inProgressSmsSession) { |
| 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); |
| return smsSession; |
| } |
| |
| /** |
| * 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 SignalStrength event |
| * |
| * @param phoneId Phone id |
| * @param signalStrength Signal strength at the time of data stall recovery |
| */ |
| public void writeSignalStrengthEvent(int phoneId, int signalStrength) { |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId) |
| .setSignalStrength(signalStrength).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. |
| */ |
| public void writeImsSetFeatureValue(int phoneId, int feature, int network, int value) { |
| TelephonySettings s = new TelephonySettings(); |
| if (network == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { |
| switch (feature) { |
| case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: |
| s.isEnhanced4GLteModeEnabled = (value != 0); |
| break; |
| case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: |
| s.isVtOverLteEnabled = (value != 0); |
| break; |
| } |
| } else if (network == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) { |
| switch (feature) { |
| case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: |
| s.isWifiCallingEnabled = (value != 0); |
| break; |
| case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: |
| 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, |
| @ImsRegistrationImplBase.ImsRegistrationTech int radioTech, |
| MmTelFeature.MmTelCapabilities capabilities) { |
| ImsCapabilities cap = new ImsCapabilities(); |
| |
| if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { |
| cap.voiceOverLte = capabilities.isCapable( |
| MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); |
| cap.videoOverLte = capabilities.isCapable( |
| MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO); |
| cap.utOverLte = capabilities.isCapable( |
| MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT); |
| |
| } else if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) { |
| cap.voiceOverWifi = capabilities.isCapable( |
| MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); |
| cap.videoOverWifi = capabilities.isCapable( |
| MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO); |
| cap.utOverWifi = capabilities.isCapable( |
| MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT); |
| } |
| |
| 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; |
| case "NON-IP": |
| return PDP_TYPE_NON_IP; |
| case "UNSTRUCTURED": |
| return PDP_TYPE_UNSTRUCTURED; |
| } |
| Rlog.e(TAG, "Unknown type: " + type); |
| return PDP_UNKNOWN; |
| } |
| |
| /** |
| * Write setup data call event |
| * |
| * @param phoneId Phone id |
| * @param radioTechnology The data call RAT |
| * @param profileId Data profile id |
| * @param apn APN in string |
| * @param protocol Data connection protocol |
| */ |
| public void writeSetupDataCall(int phoneId, int radioTechnology, int profileId, String apn, |
| int protocol) { |
| |
| RilSetupDataCall setupDataCall = new RilSetupDataCall(); |
| setupDataCall.rat = radioTechnology; |
| setupDataCall.dataProfile = profileId + 1; // off by 1 between proto and RIL constants. |
| if (apn != null) { |
| setupDataCall.apn = apn; |
| } |
| |
| setupDataCall.type = protocol + 1; |
| |
| 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; |
| switch (reason) { |
| case DataService.REQUEST_REASON_NORMAL: |
| deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_NONE; |
| break; |
| case DataService.REQUEST_REASON_SHUTDOWN: |
| deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_RADIO_OFF; |
| break; |
| case DataService.REQUEST_REASON_HANDOVER: |
| deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_HANDOVER; |
| break; |
| default: |
| deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_UNKNOWN; |
| } |
| |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDeactivateDataCall( |
| deactivateDataCall).build()); |
| } |
| |
| /** |
| * Write data call list event when connected |
| * @param phoneId Phone id |
| * @param cid Context Id, uniquely identifies the call |
| * @param apnTypeBitmask Bitmask of supported APN types |
| * @param state State of the data call event |
| */ |
| public void writeRilDataCallEvent(int phoneId, int cid, |
| int apnTypeBitmask, int state) { |
| RilDataCall[] dataCalls = new RilDataCall[1]; |
| dataCalls[0] = new RilDataCall(); |
| dataCalls[0].cid = cid; |
| dataCalls[0].apnTypeBitmask = apnTypeBitmask; |
| dataCalls[0].state = state; |
| |
| SparseArray<RilDataCall> dataCallList; |
| if (mLastRilDataCallEvents.get(phoneId) != null) { |
| // If the Data call event does not change, do not log it. |
| if (mLastRilDataCallEvents.get(phoneId).get(cid) != null |
| && Arrays.equals( |
| RilDataCall.toByteArray(mLastRilDataCallEvents.get(phoneId).get(cid)), |
| RilDataCall.toByteArray(dataCalls[0]))) { |
| return; |
| } |
| dataCallList = mLastRilDataCallEvents.get(phoneId); |
| } else { |
| dataCallList = new SparseArray<>(); |
| } |
| |
| dataCallList.put(cid, dataCalls[0]); |
| mLastRilDataCallEvents.put(phoneId, dataCallList); |
| 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, |
| String countryIso) { |
| logv("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, countryIso); |
| callSession.addEvent( |
| new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED) |
| .setRilCalls(calls) |
| ); |
| logv("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, |
| String countryIso) { |
| 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], countryIso); |
| } |
| return calls; |
| } |
| |
| private EmergencyNumberInfo convertEmergencyNumberToEmergencyNumberInfo(EmergencyNumber num) { |
| EmergencyNumberInfo emergencyNumberInfo = new EmergencyNumberInfo(); |
| emergencyNumberInfo.address = num.getNumber(); |
| emergencyNumberInfo.countryIso = num.getCountryIso(); |
| emergencyNumberInfo.mnc = num.getMnc(); |
| emergencyNumberInfo.serviceCategoriesBitmask = num.getEmergencyServiceCategoryBitmask(); |
| emergencyNumberInfo.urns = num.getEmergencyUrns().stream().toArray(String[]::new); |
| emergencyNumberInfo.numberSourcesBitmask = num.getEmergencyNumberSourceBitmask(); |
| emergencyNumberInfo.routing = num.getEmergencyCallRouting(); |
| return emergencyNumberInfo; |
| } |
| |
| private void convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call, |
| String countryIso) { |
| 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(); |
| call.preciseDisconnectCause = conn.getPreciseDisconnectCause(); |
| |
| // Emergency call metrics when call ends |
| if (conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED |
| && conn.isEmergencyCall() && conn.getEmergencyNumberInfo() != null) { |
| /** Only collect this emergency number information per sample percentage */ |
| if (ThreadLocalRandom.current().nextDouble(0, 100) |
| < getSamplePercentageForEmergencyCall(countryIso)) { |
| call.isEmergencyCall = conn.isEmergencyCall(); |
| call.emergencyNumberInfo = convertEmergencyNumberToEmergencyNumberInfo( |
| conn.getEmergencyNumberInfo()); |
| EmergencyNumberTracker emergencyNumberTracker = conn.getEmergencyNumberTracker(); |
| call.emergencyNumberDatabaseVersion = emergencyNumberTracker != null |
| ? emergencyNumberTracker.getEmergencyNumberDbVersion() |
| : TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION; |
| } |
| } |
| } |
| |
| /** |
| * 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); |
| logv("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)); |
| logv("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, |
| String countryIso) { |
| 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], countryIso); |
| callSession.addEvent( |
| new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST) |
| .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP) |
| .setRilCalls(calls)); |
| logv("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 result Data call result |
| */ |
| 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.getCause() == 0 |
| ? RilDataCallFailCause.PDP_FAIL_NONE : response.getCause()); |
| setupDataCallResponse.suggestedRetryTimeMillis = response.getSuggestedRetryTime(); |
| |
| dataCall.cid = response.getId(); |
| dataCall.type = response.getProtocolType() + 1; |
| |
| if (!TextUtils.isEmpty(response.getInterfaceName())) { |
| dataCall.iframe = response.getInterfaceName(); |
| } |
| } |
| 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 = SmsResponse.NO_ERROR_CODE; |
| 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 SMS related solicited response event |
| * |
| * @param phoneId Phone id |
| * @param errorReason Defined in {@link SmsManager} RESULT_XXX. |
| * @param messageId Unique id for this message. |
| */ |
| public synchronized void writeOnImsServiceSmsSolicitedResponse(int phoneId, |
| @ImsSmsImplBase.SendStatusResult int resultCode, int errorReason, |
| long messageId) { |
| |
| InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId); |
| if (smsSession == null) { |
| Rlog.e(TAG, "SMS session is missing"); |
| } else { |
| |
| smsSession.addEvent(new SmsSessionEventBuilder( |
| SmsSession.Event.Type.SMS_SEND_RESULT) |
| .setImsServiceErrno(resultCode) |
| .setErrorCode(errorReason) |
| .setMessageId((messageId)) |
| ); |
| |
| 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 response = (DataCallResponse) ret; |
| writeOnSetupDataCallResponse(phoneId, rilSerial, rilError, rilRequest, response); |
| 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 network validation event. |
| * @param networkValidationState the network validation state. |
| */ |
| public void writeNetworkValidate(int networkValidationState) { |
| addTelephonyEvent( |
| new TelephonyEventBuilder().setNetworkValidate(networkValidationState).build()); |
| } |
| |
| /** |
| * Write data switch event. |
| * @param subId data switch to the subscription with this id. |
| * @param dataSwitch the reason and state of data switch. |
| */ |
| public void writeDataSwitch(int subId, DataSwitch dataSwitch) { |
| int phoneId = SubscriptionManager.getPhoneId(subId); |
| addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDataSwitch(dataSwitch).build()); |
| } |
| |
| /** |
| * Write on demand data switch event. |
| * @param onDemandDataSwitch the apn and state of on demand data switch. |
| */ |
| public void writeOnDemandDataSwitch(OnDemandDataSwitch onDemandDataSwitch) { |
| addTelephonyEvent( |
| new TelephonyEventBuilder().setOnDemandDataSwitch(onDemandDataSwitch).build()); |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * Convert CallQuality to proto. |
| * |
| * @param callQuality call quality to convert |
| * @return Coverted proto |
| */ |
| public static TelephonyCallSession.Event.CallQuality toCallQualityProto( |
| CallQuality callQuality) { |
| TelephonyCallSession.Event.CallQuality cq = new TelephonyCallSession.Event.CallQuality(); |
| if (callQuality != null) { |
| cq.downlinkLevel = callQualityLevelToProtoEnum(callQuality |
| .getDownlinkCallQualityLevel()); |
| cq.uplinkLevel = callQualityLevelToProtoEnum(callQuality.getUplinkCallQualityLevel()); |
| // callDuration is reported in millis, so convert to seconds |
| cq.durationInSeconds = callQuality.getCallDuration() / 1000; |
| cq.rtpPacketsTransmitted = callQuality.getNumRtpPacketsTransmitted(); |
| cq.rtpPacketsReceived = callQuality.getNumRtpPacketsReceived(); |
| cq.rtpPacketsTransmittedLost = callQuality.getNumRtpPacketsTransmittedLost(); |
| cq.rtpPacketsNotReceived = callQuality.getNumRtpPacketsNotReceived(); |
| cq.averageRelativeJitterMillis = callQuality.getAverageRelativeJitter(); |
| cq.maxRelativeJitterMillis = callQuality.getMaxRelativeJitter(); |
| cq.codecType = convertImsCodec(callQuality.getCodecType()); |
| cq.rtpInactivityDetected = callQuality.isRtpInactivityDetected(); |
| cq.rxSilenceDetected = callQuality.isIncomingSilenceDetectedAtCallSetup(); |
| cq.txSilenceDetected = callQuality.isOutgoingSilenceDetectedAtCallSetup(); |
| } |
| return cq; |
| } |
| |
| /** |
| * Convert Call quality level into proto defined value. |
| */ |
| private static int callQualityLevelToProtoEnum(int level) { |
| if (level == CallQuality.CALL_QUALITY_EXCELLENT) { |
| return TelephonyCallSession.Event.CallQuality.CallQualityLevel.EXCELLENT; |
| } else if (level == CallQuality.CALL_QUALITY_GOOD) { |
| return TelephonyCallSession.Event.CallQuality.CallQualityLevel.GOOD; |
| } else if (level == CallQuality.CALL_QUALITY_FAIR) { |
| return TelephonyCallSession.Event.CallQuality.CallQualityLevel.FAIR; |
| } else if (level == CallQuality.CALL_QUALITY_POOR) { |
| return TelephonyCallSession.Event.CallQuality.CallQualityLevel.POOR; |
| } else if (level == CallQuality.CALL_QUALITY_BAD) { |
| return TelephonyCallSession.Event.CallQuality.CallQualityLevel.BAD; |
| } else if (level == CallQuality.CALL_QUALITY_NOT_AVAILABLE) { |
| return TelephonyCallSession.Event.CallQuality.CallQualityLevel.NOT_AVAILABLE; |
| } else { |
| return TelephonyCallSession.Event.CallQuality.CallQualityLevel.UNDEFINED; |
| } |
| } |
| |
| /** |
| * Write IMS call end event |
| * |
| * @param phoneId Phone id |
| * @param session IMS call session |
| * @param reasonInfo Call end reason |
| * @param cqm Call Quality Metrics |
| * @param emergencyNumber Emergency Number Info |
| * @param countryIso Network country iso |
| * @param emergencyNumberDatabaseVersion Emergency Number Database Version |
| */ |
| public void writeOnImsCallTerminated(int phoneId, ImsCallSession session, |
| ImsReasonInfo reasonInfo, CallQualityMetrics cqm, |
| EmergencyNumber emergencyNumber, String countryIso, |
| int emergencyNumberDatabaseVersion) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "Call session is missing"); |
| } else { |
| CallSessionEventBuilder callSessionEvent = new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED); |
| callSessionEvent.setCallIndex(getCallId(session)); |
| callSessionEvent.setImsReasonInfo(toImsReasonInfoProto(reasonInfo)); |
| |
| if (cqm != null) { |
| callSessionEvent.setCallQualitySummaryDl(cqm.getCallQualitySummaryDl()) |
| .setCallQualitySummaryUl(cqm.getCallQualitySummaryUl()); |
| } |
| |
| if (emergencyNumber != null) { |
| /** Only collect this emergency number information per sample percentage */ |
| if (ThreadLocalRandom.current().nextDouble(0, 100) |
| < getSamplePercentageForEmergencyCall(countryIso)) { |
| callSessionEvent.setIsImsEmergencyCall(true); |
| callSessionEvent.setImsEmergencyNumberInfo( |
| convertEmergencyNumberToEmergencyNumberInfo(emergencyNumber)); |
| callSessionEvent.setEmergencyNumberDatabaseVersion( |
| emergencyNumberDatabaseVersion); |
| } |
| } |
| callSession.addEvent(callSessionEvent); |
| } |
| } |
| |
| /** |
| * 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 Send SMS event using ImsService. Expecting response from |
| * {@link #writeOnSmsSolicitedResponse}. |
| * |
| * @param phoneId Phone id |
| * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or |
| * {@link SmsMessage#FORMAT_3GPP2}. |
| * @param resultCode The result of sending the new SMS to the vendor layer to be sent to the |
| * carrier network. |
| */ |
| public synchronized void writeImsServiceSendSms(int phoneId, String format, |
| @ImsSmsImplBase.SendStatusResult int resultCode) { |
| InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId); |
| smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND) |
| .setTech(SmsSession.Event.Tech.SMS_IMS) |
| .setImsServiceErrno(resultCode) |
| .setFormat(convertSmsFormat(format)) |
| ); |
| |
| smsSession.increaseExpectedResponse(); |
| } |
| |
| /** |
| * 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 |
| * @param serialNumber Serial number of the message |
| * @param deliveredTimestamp Message's delivered timestamp |
| */ |
| public synchronized void writeNewCBSms(int phoneId, int format, int priority, boolean isCMAS, |
| boolean isETWS, int serviceCategory, int serialNumber, |
| long deliveredTimestamp) { |
| 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; |
| cbm.serialNumber = serialNumber; |
| cbm.deliveredTimestampMillis = deliveredTimestamp; |
| |
| smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.CB_SMS_RECEIVED) |
| .setCellBroadcastMessage(cbm) |
| ); |
| |
| finishSmsSessionIfNeeded(smsSession); |
| } |
| |
| /** |
| * Write an incoming multi-part SMS that was discarded because some parts were missing |
| * |
| * @param phoneId Phone id |
| * @param format SMS format. Either 3GPP or 3GPP2. |
| * @param receivedCount Number of received parts. |
| * @param totalCount Total number of parts of the SMS. |
| */ |
| public void writeDroppedIncomingMultipartSms(int phoneId, String format, |
| int receivedCount, int totalCount) { |
| logv("Logged dropped multipart SMS: received " + receivedCount |
| + " out of " + totalCount); |
| |
| SmsSession.Event.IncompleteSms details = new SmsSession.Event.IncompleteSms(); |
| details.receivedParts = receivedCount; |
| details.totalParts = totalCount; |
| |
| InProgressSmsSession smsSession = startNewSmsSession(phoneId); |
| smsSession.addEvent( |
| new SmsSessionEventBuilder(SmsSession.Event.Type.INCOMPLETE_SMS_RECEIVED) |
| .setFormat(convertSmsFormat(format)) |
| .setIncompleteSms(details)); |
| |
| finishSmsSession(smsSession); |
| } |
| |
| /** |
| * Write a generic SMS of any type |
| * |
| * @param phoneId Phone id |
| * @param type Type of the SMS. |
| * @param format SMS format. Either 3GPP or 3GPP2. |
| * @param success Indicates if the SMS-PP was successfully delivered to the USIM. |
| */ |
| private void writeIncomingSmsWithType(int phoneId, int type, String format, boolean success) { |
| InProgressSmsSession smsSession = startNewSmsSession(phoneId); |
| smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED) |
| .setFormat(convertSmsFormat(format)) |
| .setSmsType(type) |
| .setErrorCode(success ? SmsManager.RESULT_ERROR_NONE : |
| SmsManager.RESULT_ERROR_GENERIC_FAILURE)); |
| finishSmsSession(smsSession); |
| } |
| |
| /** |
| * Write an incoming SMS-PP for the USIM |
| * |
| * @param phoneId Phone id |
| * @param format SMS format. Either 3GPP or 3GPP2. |
| * @param success Indicates if the SMS-PP was successfully delivered to the USIM. |
| */ |
| public void writeIncomingSMSPP(int phoneId, String format, boolean success) { |
| logv("Logged SMS-PP session. Result = " + success); |
| writeIncomingSmsWithType(phoneId, |
| SmsSession.Event.SmsType.SMS_TYPE_SMS_PP, format, success); |
| } |
| |
| /** |
| * Write an incoming SMS to update voicemail indicator |
| * |
| * @param phoneId Phone id |
| * @param format SMS format. Either 3GPP or 3GPP2. |
| */ |
| public void writeIncomingVoiceMailSms(int phoneId, String format) { |
| logv("Logged VoiceMail message."); |
| writeIncomingSmsWithType(phoneId, |
| SmsSession.Event.SmsType.SMS_TYPE_VOICEMAIL_INDICATION, format, true); |
| } |
| |
| /** |
| * Write an incoming SMS of type 0 |
| * |
| * @param phoneId Phone id |
| * @param format SMS format. Either 3GPP or 3GPP2. |
| */ |
| public void writeIncomingSmsTypeZero(int phoneId, String format) { |
| logv("Logged Type-0 SMS message."); |
| writeIncomingSmsWithType(phoneId, |
| SmsSession.Event.SmsType.SMS_TYPE_ZERO, format, true); |
| } |
| |
| /** |
| * Write a successful incoming SMS session |
| * |
| * @param phoneId Phone id |
| * @param type Type of the SMS. |
| * @param smsOverIms true if the SMS was received over SMS, false otherwise |
| * @param format SMS format. Either 3GPP or 3GPP2. |
| * @param timestamps array with timestamps of each incoming SMS part. It contains a single |
| * @param blocked indicates if the message was blocked or not. |
| * @param success Indicates if the SMS-PP was successfully delivered to the USIM. |
| * @param messageId Unique id for this message. |
| */ |
| private void writeIncomingSmsSessionWithType(int phoneId, int type, boolean smsOverIms, |
| String format, long[] timestamps, boolean blocked, boolean success, |
| long messageId) { |
| logv("Logged SMS session consisting of " + timestamps.length |
| + " parts, over IMS = " + smsOverIms |
| + " blocked = " + blocked |
| + " type = " + type |
| + " messageId = " + messageId); |
| |
| InProgressSmsSession smsSession = startNewSmsSession(phoneId); |
| for (long time : timestamps) { |
| SmsSessionEventBuilder eventBuilder = |
| new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED) |
| .setFormat(convertSmsFormat(format)) |
| .setTech(smsOverIms ? SmsSession.Event.Tech.SMS_IMS : |
| SmsSession.Event.Tech.SMS_GSM) |
| .setErrorCode(success ? SmsManager.RESULT_ERROR_NONE : |
| SmsManager.RESULT_ERROR_GENERIC_FAILURE) |
| .setSmsType(type) |
| .setBlocked(blocked) |
| .setMessageId(messageId); |
| smsSession.addEvent(time, eventBuilder); |
| } |
| finishSmsSession(smsSession); |
| } |
| |
| /** |
| * Write an incoming WAP-PUSH message. |
| * |
| * @param phoneId Phone id |
| * @param smsOverIms true if the SMS was received over SMS, false otherwise |
| * @param format SMS format. Either 3GPP or 3GPP2. |
| * @param timestamps array with timestamps of each incoming SMS part. It contains a single |
| * @param success Indicates if the SMS-PP was successfully delivered to the USIM. |
| * @param messageId Unique id for this message. |
| */ |
| public void writeIncomingWapPush(int phoneId, boolean smsOverIms, String format, |
| long[] timestamps, boolean success, long messageId) { |
| writeIncomingSmsSessionWithType(phoneId, SmsSession.Event.SmsType.SMS_TYPE_WAP_PUSH, |
| smsOverIms, format, timestamps, false, success, messageId); |
| } |
| |
| /** |
| * Write a successful incoming SMS session |
| * |
| * @param phoneId Phone id |
| * @param smsOverIms true if the SMS was received over SMS, false otherwise |
| * @param format SMS format. Either 3GPP or 3GPP2. |
| * @param timestamps array with timestamps of each incoming SMS part. It contains a single |
| * @param blocked indicates if the message was blocked or not. |
| * @param messageId Unique id for this message. |
| */ |
| public void writeIncomingSmsSession(int phoneId, boolean smsOverIms, String format, |
| long[] timestamps, boolean blocked, long messageId) { |
| writeIncomingSmsSessionWithType(phoneId, SmsSession.Event.SmsType.SMS_TYPE_NORMAL, |
| smsOverIms, format, timestamps, blocked, true, messageId); |
| } |
| |
| /** |
| * Write an error incoming SMS |
| * |
| * @param phoneId Phone id |
| * @param smsOverIms true if the SMS was received over SMS, false otherwise |
| * @param result Indicates the reason of the failure. |
| */ |
| public void writeIncomingSmsError(int phoneId, boolean smsOverIms, int result) { |
| logv("Incoming SMS error = " + result); |
| |
| int smsError = SmsManager.RESULT_ERROR_GENERIC_FAILURE; |
| switch (result) { |
| case Intents.RESULT_SMS_HANDLED: |
| // This should not happen. |
| return; |
| case Intents.RESULT_SMS_OUT_OF_MEMORY: |
| smsError = SmsManager.RESULT_NO_MEMORY; |
| break; |
| case Intents.RESULT_SMS_UNSUPPORTED: |
| smsError = SmsManager.RESULT_REQUEST_NOT_SUPPORTED; |
| break; |
| case Intents.RESULT_SMS_GENERIC_ERROR: |
| default: |
| smsError = SmsManager.RESULT_ERROR_GENERIC_FAILURE; |
| break; |
| } |
| |
| InProgressSmsSession smsSession = startNewSmsSession(phoneId); |
| |
| SmsSessionEventBuilder eventBuilder = |
| new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED) |
| .setErrorCode(smsError) |
| .setTech(smsOverIms ? SmsSession.Event.Tech.SMS_IMS : |
| SmsSession.Event.Tech.SMS_GSM); |
| smsSession.addEvent(eventBuilder); |
| finishSmsSession(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); |
| } |
| |
| /** |
| * Write carrier identification matching event |
| * |
| * @param phoneId Phone id |
| * @param version Carrier table version |
| * @param cid Unique Carrier Id |
| * @param unknownMcmnc MCC and MNC that map to this carrier |
| * @param unknownGid1 Group id level 1 |
| * @param simInfo Subscription info |
| */ |
| public void writeCarrierIdMatchingEvent(int phoneId, int version, int cid, |
| String unknownMcmnc, String unknownGid1, |
| CarrierResolver.CarrierMatchingRule simInfo) { |
| final CarrierIdMatching carrierIdMatching = new CarrierIdMatching(); |
| final CarrierIdMatchingResult carrierIdMatchingResult = new CarrierIdMatchingResult(); |
| |
| // fill in information for unknown mccmnc and gid1 for unidentified carriers. |
| if (cid != TelephonyManager.UNKNOWN_CARRIER_ID) { |
| // Successful matching event if result only has carrierId |
| carrierIdMatchingResult.carrierId = cid; |
| // Unknown Gid1 event if result only has carrierId, gid1 and mccmnc |
| if (unknownGid1 != null) { |
| carrierIdMatchingResult.unknownMccmnc = unknownMcmnc; |
| carrierIdMatchingResult.unknownGid1 = unknownGid1; |
| } |
| } else { |
| // Unknown mccmnc event if result only has mccmnc |
| if (unknownMcmnc != null) { |
| carrierIdMatchingResult.unknownMccmnc = unknownMcmnc; |
| } |
| } |
| |
| // fill in complete matching information from the SIM. |
| carrierIdMatchingResult.mccmnc = TelephonyUtils.emptyIfNull(simInfo.mccMnc); |
| carrierIdMatchingResult.spn = TelephonyUtils.emptyIfNull(simInfo.spn); |
| carrierIdMatchingResult.pnn = TelephonyUtils.emptyIfNull(simInfo.plmn); |
| carrierIdMatchingResult.gid1 = TelephonyUtils.emptyIfNull(simInfo.gid1); |
| carrierIdMatchingResult.gid2 = TelephonyUtils.emptyIfNull(simInfo.gid2); |
| carrierIdMatchingResult.imsiPrefix = TelephonyUtils.emptyIfNull(simInfo.imsiPrefixPattern); |
| carrierIdMatchingResult.iccidPrefix = TelephonyUtils.emptyIfNull(simInfo.iccidPrefix); |
| carrierIdMatchingResult.preferApn = TelephonyUtils.emptyIfNull(simInfo.apn); |
| if (simInfo.privilegeAccessRule != null) { |
| carrierIdMatchingResult.privilegeAccessRule = |
| simInfo.privilegeAccessRule.stream().toArray(String[]::new); |
| } |
| |
| carrierIdMatching.cidTableVersion = version; |
| carrierIdMatching.result = carrierIdMatchingResult; |
| |
| TelephonyEvent event = new TelephonyEventBuilder(phoneId).setCarrierIdMatching( |
| carrierIdMatching).build(); |
| mLastCarrierId.put(phoneId, carrierIdMatching); |
| addTelephonyEvent(event); |
| } |
| |
| /** |
| * Write emergency number update event |
| * |
| * @param emergencyNumber Updated emergency number |
| */ |
| public void writeEmergencyNumberUpdateEvent(int phoneId, EmergencyNumber emergencyNumber, |
| int emergencyNumberDatabaseVersion) { |
| if (emergencyNumber == null) { |
| return; |
| } |
| final EmergencyNumberInfo emergencyNumberInfo = |
| convertEmergencyNumberToEmergencyNumberInfo(emergencyNumber); |
| |
| TelephonyEvent event = new TelephonyEventBuilder(phoneId).setUpdatedEmergencyNumber( |
| emergencyNumberInfo, emergencyNumberDatabaseVersion).build(); |
| addTelephonyEvent(event); |
| } |
| |
| /** |
| * Write network capabilities changed event |
| * |
| * @param phoneId Phone id |
| * @param networkCapabilities Network capabilities |
| */ |
| public void writeNetworkCapabilitiesChangedEvent(int phoneId, |
| NetworkCapabilities networkCapabilities) { |
| final NetworkCapabilitiesInfo caps = new NetworkCapabilitiesInfo(); |
| caps.isNetworkUnmetered = networkCapabilities.hasCapability( |
| NetworkCapabilities.NET_CAPABILITY_NOT_METERED); |
| |
| TelephonyEvent event = new TelephonyEventBuilder(phoneId) |
| .setNetworkCapabilities(caps).build(); |
| mLastNetworkCapabilitiesInfos.put(phoneId, caps); |
| addTelephonyEvent(event); |
| } |
| |
| /** |
| * Convert SMS format |
| */ |
| private int convertSmsFormat(String format) { |
| int formatCode = SmsSession.Event.Format.SMS_FORMAT_UNKNOWN; |
| switch (format) { |
| case SmsMessage.FORMAT_3GPP : { |
| formatCode = SmsSession.Event.Format.SMS_FORMAT_3GPP; |
| break; |
| } |
| case SmsMessage.FORMAT_3GPP2: { |
| formatCode = SmsSession.Event.Format.SMS_FORMAT_3GPP2; |
| break; |
| } |
| } |
| return formatCode; |
| } |
| |
| /** |
| * Convert IMS audio codec into proto defined value |
| * |
| * @param c IMS codec value |
| * @return Codec value defined in call session proto |
| */ |
| private static int convertImsCodec(int c) { |
| switch (c) { |
| case ImsStreamMediaProfile.AUDIO_QUALITY_AMR: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR_WB; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_QCELP13K; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_B; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_WB; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_NW; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_EFR; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_FR; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_HR; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_G711U: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711U; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_G723: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G723; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_G711A: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711A; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_G722: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G722; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_G711AB: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711AB; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_G729: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G729; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_NB; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_WB; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_SWB; |
| case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_FB; |
| default: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_UNKNOWN; |
| } |
| } |
| |
| /** |
| * Convert GSM/CDMA audio codec into proto defined value |
| * |
| * @param c GSM/CDMA codec value |
| * @return Codec value defined in call session proto |
| */ |
| private int convertGsmCdmaCodec(int c) { |
| switch (c) { |
| case DriverCall.AUDIO_QUALITY_AMR: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR; |
| case DriverCall.AUDIO_QUALITY_AMR_WB: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR_WB; |
| case DriverCall.AUDIO_QUALITY_GSM_EFR: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_EFR; |
| case DriverCall.AUDIO_QUALITY_GSM_FR: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_FR; |
| case DriverCall.AUDIO_QUALITY_GSM_HR: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_HR; |
| case DriverCall.AUDIO_QUALITY_EVRC: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC; |
| case DriverCall.AUDIO_QUALITY_EVRC_B: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_B; |
| case DriverCall.AUDIO_QUALITY_EVRC_WB: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_WB; |
| case DriverCall.AUDIO_QUALITY_EVRC_NW: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_NW; |
| default: |
| return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_UNKNOWN; |
| } |
| } |
| |
| /** |
| * Write audio codec event |
| * |
| * @param phoneId Phone id |
| * @param session IMS call session |
| */ |
| public void writeAudioCodecIms(int phoneId, ImsCallSession session) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "Call session is missing"); |
| return; |
| } |
| |
| ImsCallProfile localCallProfile = session.getLocalCallProfile(); |
| if (localCallProfile != null) { |
| int codec = convertImsCodec(localCallProfile.mMediaProfile.mAudioQuality); |
| callSession.addEvent(new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.AUDIO_CODEC) |
| .setCallIndex(getCallId(session)) |
| .setAudioCodec(codec)); |
| |
| logv("Logged Audio Codec event. Value: " + codec); |
| } |
| } |
| |
| /** |
| * Write audio codec event |
| * |
| * @param phoneId Phone id |
| * @param audioQuality Audio quality value |
| */ |
| public void writeAudioCodecGsmCdma(int phoneId, int audioQuality) { |
| InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); |
| if (callSession == null) { |
| Rlog.e(TAG, "Call session is missing"); |
| return; |
| } |
| |
| int codec = convertGsmCdmaCodec(audioQuality); |
| callSession.addEvent(new CallSessionEventBuilder( |
| TelephonyCallSession.Event.Type.AUDIO_CODEC) |
| .setAudioCodec(codec)); |
| |
| logv("Logged Audio Codec event. Value: " + codec); |
| } |
| |
| //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) {} |
| |
| /** |
| * Get the sample percentage of collecting metrics based on countries' population. |
| * |
| * The larger population the country has, the lower percentage we use to collect this |
| * metrics. Since the exact population changes frequently, buckets of the population are used |
| * instead of its exact number. Seven different levels of sampling percentage are assigned |
| * based on the scale of population for countries. |
| */ |
| private double getSamplePercentageForEmergencyCall(String countryIso) { |
| String countriesFor1Percentage = "cn,in"; |
| String countriesFor5Percentage = "us,id,br,pk,ng,bd,ru,mx,jp,et,ph,eg,vn,cd,tr,ir,de"; |
| String countriesFor15Percentage = "th,gb,fr,tz,it,za,mm,ke,kr,co,es,ug,ar,ua,dz,sd,iq"; |
| String countriesFor25Percentage = "pl,ca,af,ma,sa,pe,uz,ve,my,ao,mz,gh,np,ye,mg,kp,cm"; |
| String countriesFor35Percentage = "au,tw,ne,lk,bf,mw,ml,ro,kz,sy,cl,zm,gt,zw,nl,ec,sn"; |
| String countriesFor45Percentage = "kh,td,so,gn,ss,rw,bj,tn,bi,be,cu,bo,ht,gr,do,cz,pt"; |
| if (countriesFor1Percentage.contains(countryIso)) { |
| return 1; |
| } else if (countriesFor5Percentage.contains(countryIso)) { |
| return 5; |
| } else if (countriesFor15Percentage.contains(countryIso)) { |
| return 15; |
| } else if (countriesFor25Percentage.contains(countryIso)) { |
| return 25; |
| } else if (countriesFor35Percentage.contains(countryIso)) { |
| return 35; |
| } else if (countriesFor45Percentage.contains(countryIso)) { |
| return 45; |
| } else { |
| return 50; |
| } |
| } |
| |
| private static int mapSimStateToProto(int simState) { |
| switch (simState) { |
| case TelephonyManager.SIM_STATE_ABSENT: |
| return SimState.SIM_STATE_ABSENT; |
| case TelephonyManager.SIM_STATE_LOADED: |
| return SimState.SIM_STATE_LOADED; |
| default: |
| return SimState.SIM_STATE_UNKNOWN; |
| } |
| } |
| } |