1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony.metrics; 18 19 import static android.text.format.DateUtils.MINUTE_IN_MILLIS; 20 21 import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_IMS; 22 import static com.android.internal.telephony.InboundSmsHandler.SOURCE_NOT_INJECTED; 23 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER; 24 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS; 25 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL; 26 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL; 27 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP; 28 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND; 29 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND; 30 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS; 31 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS; 32 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE; 33 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL; 34 import static com.android.internal.telephony.dataconnection.LinkBandwidthEstimator.NUM_SIGNAL_LEVEL; 35 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IP; 36 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6; 37 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV6; 38 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_NON_IP; 39 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_PPP; 40 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_UNSTRUCTURED; 41 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_UNKNOWN; 42 43 import android.content.Context; 44 import android.net.NetworkCapabilities; 45 import android.os.BatteryStatsManager; 46 import android.os.Build; 47 import android.os.SystemClock; 48 import android.os.SystemProperties; 49 import android.provider.Telephony.Sms.Intents; 50 import android.telephony.AccessNetworkConstants; 51 import android.telephony.Annotation.RadioPowerState; 52 import android.telephony.CallQuality; 53 import android.telephony.DisconnectCause; 54 import android.telephony.NetworkRegistrationInfo; 55 import android.telephony.ServiceState; 56 import android.telephony.SmsManager; 57 import android.telephony.SmsMessage; 58 import android.telephony.SubscriptionInfo; 59 import android.telephony.SubscriptionManager; 60 import android.telephony.TelephonyHistogram; 61 import android.telephony.TelephonyManager; 62 import android.telephony.TelephonyManager.PrefNetworkMode; 63 import android.telephony.data.DataCallResponse; 64 import android.telephony.data.DataService; 65 import android.telephony.emergency.EmergencyNumber; 66 import android.telephony.ims.ImsCallProfile; 67 import android.telephony.ims.ImsCallSession; 68 import android.telephony.ims.ImsReasonInfo; 69 import android.telephony.ims.ImsStreamMediaProfile; 70 import android.telephony.ims.feature.MmTelFeature; 71 import android.telephony.ims.stub.ImsRegistrationImplBase; 72 import android.telephony.ims.stub.ImsSmsImplBase; 73 import android.text.TextUtils; 74 import android.util.ArrayMap; 75 import android.util.Base64; 76 import android.util.SparseArray; 77 78 import com.android.internal.telephony.CarrierResolver; 79 import com.android.internal.telephony.DriverCall; 80 import com.android.internal.telephony.GsmCdmaConnection; 81 import com.android.internal.telephony.InboundSmsHandler; 82 import com.android.internal.telephony.PhoneConstants; 83 import com.android.internal.telephony.RIL; 84 import com.android.internal.telephony.RILConstants; 85 import com.android.internal.telephony.SmsController; 86 import com.android.internal.telephony.SmsResponse; 87 import com.android.internal.telephony.UUSInfo; 88 import com.android.internal.telephony.dataconnection.LinkBandwidthEstimator; 89 import com.android.internal.telephony.emergency.EmergencyNumberTracker; 90 import com.android.internal.telephony.imsphone.ImsPhoneCall; 91 import com.android.internal.telephony.nano.TelephonyProto; 92 import com.android.internal.telephony.nano.TelephonyProto.ActiveSubscriptionInfo; 93 import com.android.internal.telephony.nano.TelephonyProto.BandwidthEstimatorStats; 94 import com.android.internal.telephony.nano.TelephonyProto.EmergencyNumberInfo; 95 import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities; 96 import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState; 97 import com.android.internal.telephony.nano.TelephonyProto.ModemPowerStats; 98 import com.android.internal.telephony.nano.TelephonyProto.RilDataCall; 99 import com.android.internal.telephony.nano.TelephonyProto.SimState; 100 import com.android.internal.telephony.nano.TelephonyProto.SmsSession; 101 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession; 102 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.CallState; 103 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall; 104 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall.Type; 105 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent; 106 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatching; 107 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatchingResult; 108 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierKeyChange; 109 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.DataSwitch; 110 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart; 111 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.NetworkCapabilitiesInfo; 112 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.OnDemandDataSwitch; 113 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RadioState; 114 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall; 115 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall.DeactivateReason; 116 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall; 117 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse; 118 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse.RilDataCallFailCause; 119 import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog; 120 import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState; 121 import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings; 122 import com.android.internal.telephony.nano.TelephonyProto.TimeInterval; 123 import com.android.internal.telephony.protobuf.nano.MessageNano; 124 import com.android.internal.telephony.util.TelephonyUtils; 125 import com.android.internal.util.IndentingPrintWriter; 126 import com.android.telephony.Rlog; 127 128 import java.io.FileDescriptor; 129 import java.io.PrintWriter; 130 import java.text.DecimalFormat; 131 import java.util.ArrayDeque; 132 import java.util.ArrayList; 133 import java.util.Arrays; 134 import java.util.Deque; 135 import java.util.List; 136 import java.util.Map; 137 import java.util.concurrent.ThreadLocalRandom; 138 139 /** 140 * Telephony metrics holds all metrics events and convert it into telephony proto buf. 141 * @hide 142 */ 143 public class TelephonyMetrics { 144 145 private static final String TAG = TelephonyMetrics.class.getSimpleName(); 146 147 private static final boolean DBG = true; 148 private static final boolean VDBG = false; // STOPSHIP if true 149 150 /** Maximum telephony events stored */ 151 private static final int MAX_TELEPHONY_EVENTS = 1000; 152 153 /** Maximum call sessions stored */ 154 private static final int MAX_COMPLETED_CALL_SESSIONS = 50; 155 156 /** Maximum sms sessions stored */ 157 private static final int MAX_COMPLETED_SMS_SESSIONS = 500; 158 159 /** For reducing the timing precision for privacy purposes */ 160 private static final int SESSION_START_PRECISION_MINUTES = 5; 161 162 /** The TelephonyMetrics singleton instance */ 163 private static TelephonyMetrics sInstance; 164 165 /** Telephony events */ 166 private final Deque<TelephonyEvent> mTelephonyEvents = new ArrayDeque<>(); 167 168 /** 169 * In progress call sessions. Note that each phone can only have up to 1 in progress call 170 * session (might contains multiple calls). Having a sparse array in case we need to support 171 * DSDA in the future. 172 */ 173 private final SparseArray<InProgressCallSession> mInProgressCallSessions = new SparseArray<>(); 174 175 /** The completed call sessions */ 176 private final Deque<TelephonyCallSession> mCompletedCallSessions = new ArrayDeque<>(); 177 178 /** The in-progress SMS sessions. When finished, it will be moved into the completed sessions */ 179 private final SparseArray<InProgressSmsSession> mInProgressSmsSessions = new SparseArray<>(); 180 181 /** The completed SMS sessions */ 182 private final Deque<SmsSession> mCompletedSmsSessions = new ArrayDeque<>(); 183 184 /** Last service state. This is for injecting the base of a new log or a new call/sms session */ 185 private final SparseArray<TelephonyServiceState> mLastServiceState = new SparseArray<>(); 186 187 /** 188 * Last ims capabilities. This is for injecting the base of a new log or a new call/sms session 189 */ 190 private final SparseArray<ImsCapabilities> mLastImsCapabilities = new SparseArray<>(); 191 192 /** 193 * Last IMS connection state. This is for injecting the base of a new log or a new call/sms 194 * session 195 */ 196 private final SparseArray<ImsConnectionState> mLastImsConnectionState = new SparseArray<>(); 197 198 /** Last settings state. This is for deduping same settings event logged. */ 199 private final SparseArray<TelephonySettings> mLastSettings = new SparseArray<>(); 200 201 /** Last sim state, indexed by phone id. */ 202 private final SparseArray<Integer> mLastSimState = new SparseArray<>(); 203 204 /** Last radio state, indexed by phone id. */ 205 private final SparseArray<Integer> mLastRadioState = new SparseArray<>(); 206 207 /** Last active subscription information, indexed by phone id. */ 208 private final SparseArray<ActiveSubscriptionInfo> mLastActiveSubscriptionInfos = 209 new SparseArray<>(); 210 211 /** 212 * The last modem state represent by a bitmap, the i-th bit(LSB) indicates the i-th modem 213 * state(0 - disabled, 1 - enabled). 214 * 215 * TODO: initialize the enabled modem bitmap when it's possible to get the modem state. 216 */ 217 private int mLastEnabledModemBitmap = (1 << TelephonyManager.getDefault().getPhoneCount()) - 1; 218 219 /** Last carrier id matching. */ 220 private final SparseArray<CarrierIdMatching> mLastCarrierId = new SparseArray<>(); 221 222 /** Last NetworkCapabilitiesInfo, indexed by phone id. */ 223 private final SparseArray<NetworkCapabilitiesInfo> mLastNetworkCapabilitiesInfos = 224 new SparseArray<>(); 225 226 /** Last RilDataCall Events (indexed by cid), indexed by phone id */ 227 private final SparseArray<SparseArray<RilDataCall>> mLastRilDataCallEvents = 228 new SparseArray<>(); 229 230 /** List of Tx and Rx Bandwidth estimation stats maps */ 231 private final List<Map<String, BwEstimationStats>> mBwEstStatsMapList = new ArrayList<>( 232 Arrays.asList(new ArrayMap<>(), new ArrayMap<>())); 233 234 /** The start system time of the TelephonyLog in milliseconds*/ 235 private long mStartSystemTimeMs; 236 237 /** The start elapsed time of the TelephonyLog in milliseconds*/ 238 private long mStartElapsedTimeMs; 239 240 /** Indicating if some of the telephony events are dropped in this log */ 241 private boolean mTelephonyEventsDropped = false; 242 243 private Context mContext; 244 TelephonyMetrics()245 public TelephonyMetrics() { 246 mStartSystemTimeMs = System.currentTimeMillis(); 247 mStartElapsedTimeMs = SystemClock.elapsedRealtime(); 248 } 249 250 /** 251 * Get the singleton instance of telephony metrics. 252 * 253 * @return The instance 254 */ getInstance()255 public synchronized static TelephonyMetrics getInstance() { 256 if (sInstance == null) { 257 sInstance = new TelephonyMetrics(); 258 } 259 260 return sInstance; 261 } 262 263 /** 264 * Set the context for telephony metrics. 265 * 266 * @param context Context 267 * @hide 268 */ setContext(Context context)269 public void setContext(Context context) { 270 mContext = context; 271 } 272 273 /** 274 * Dump the state of various objects, add calls to other objects as desired. 275 * 276 * @param fd File descriptor 277 * @param pw Print writer 278 * @param args Arguments 279 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)280 public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 281 if (args != null && args.length > 0) { 282 boolean reset = true; 283 if (args.length > 1 && "--keep".equals(args[1])) { 284 reset = false; 285 } 286 287 switch (args[0]) { 288 case "--metrics": 289 printAllMetrics(pw); 290 break; 291 case "--metricsproto": 292 pw.println(convertProtoToBase64String(buildProto())); 293 if (reset) { 294 reset(); 295 } 296 break; 297 case "--metricsprototext": 298 pw.println(buildProto().toString()); 299 break; 300 } 301 } 302 } 303 logv(String log)304 private void logv(String log) { 305 if (VDBG) { 306 Rlog.v(TAG, log); 307 } 308 } 309 310 /** 311 * Convert the telephony event to string 312 * 313 * @param event The event in integer 314 * @return The event in string 315 */ telephonyEventToString(int event)316 private static String telephonyEventToString(int event) { 317 switch (event) { 318 case TelephonyEvent.Type.UNKNOWN: 319 return "UNKNOWN"; 320 case TelephonyEvent.Type.SETTINGS_CHANGED: 321 return "SETTINGS_CHANGED"; 322 case TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED: 323 return "RIL_SERVICE_STATE_CHANGED"; 324 case TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED: 325 return "IMS_CONNECTION_STATE_CHANGED"; 326 case TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED: 327 return "IMS_CAPABILITIES_CHANGED"; 328 case TelephonyEvent.Type.DATA_CALL_SETUP: 329 return "DATA_CALL_SETUP"; 330 case TelephonyEvent.Type.DATA_CALL_SETUP_RESPONSE: 331 return "DATA_CALL_SETUP_RESPONSE"; 332 case TelephonyEvent.Type.DATA_CALL_LIST_CHANGED: 333 return "DATA_CALL_LIST_CHANGED"; 334 case TelephonyEvent.Type.DATA_CALL_DEACTIVATE: 335 return "DATA_CALL_DEACTIVATE"; 336 case TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE: 337 return "DATA_CALL_DEACTIVATE_RESPONSE"; 338 case TelephonyEvent.Type.DATA_STALL_ACTION: 339 return "DATA_STALL_ACTION"; 340 case TelephonyEvent.Type.MODEM_RESTART: 341 return "MODEM_RESTART"; 342 case TelephonyEvent.Type.CARRIER_ID_MATCHING: 343 return "CARRIER_ID_MATCHING"; 344 case TelephonyEvent.Type.NITZ_TIME: 345 return "NITZ_TIME"; 346 case TelephonyEvent.Type.EMERGENCY_NUMBER_REPORT: 347 return "EMERGENCY_NUMBER_REPORT"; 348 case TelephonyEvent.Type.NETWORK_CAPABILITIES_CHANGED: 349 return "NETWORK_CAPABILITIES_CHANGED"; 350 default: 351 return Integer.toString(event); 352 } 353 } 354 355 /** 356 * Convert the call session event into string 357 * 358 * @param event The event in integer 359 * @return The event in String 360 */ callSessionEventToString(int event)361 private static String callSessionEventToString(int event) { 362 switch (event) { 363 case TelephonyCallSession.Event.Type.EVENT_UNKNOWN: 364 return "EVENT_UNKNOWN"; 365 case TelephonyCallSession.Event.Type.SETTINGS_CHANGED: 366 return "SETTINGS_CHANGED"; 367 case TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED: 368 return "RIL_SERVICE_STATE_CHANGED"; 369 case TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED: 370 return "IMS_CONNECTION_STATE_CHANGED"; 371 case TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED: 372 return "IMS_CAPABILITIES_CHANGED"; 373 case TelephonyCallSession.Event.Type.DATA_CALL_LIST_CHANGED: 374 return "DATA_CALL_LIST_CHANGED"; 375 case TelephonyCallSession.Event.Type.RIL_REQUEST: 376 return "RIL_REQUEST"; 377 case TelephonyCallSession.Event.Type.RIL_RESPONSE: 378 return "RIL_RESPONSE"; 379 case TelephonyCallSession.Event.Type.RIL_CALL_RING: 380 return "RIL_CALL_RING"; 381 case TelephonyCallSession.Event.Type.RIL_CALL_SRVCC: 382 return "RIL_CALL_SRVCC"; 383 case TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED: 384 return "RIL_CALL_LIST_CHANGED"; 385 case TelephonyCallSession.Event.Type.IMS_COMMAND: 386 return "IMS_COMMAND"; 387 case TelephonyCallSession.Event.Type.IMS_COMMAND_RECEIVED: 388 return "IMS_COMMAND_RECEIVED"; 389 case TelephonyCallSession.Event.Type.IMS_COMMAND_FAILED: 390 return "IMS_COMMAND_FAILED"; 391 case TelephonyCallSession.Event.Type.IMS_COMMAND_COMPLETE: 392 return "IMS_COMMAND_COMPLETE"; 393 case TelephonyCallSession.Event.Type.IMS_CALL_RECEIVE: 394 return "IMS_CALL_RECEIVE"; 395 case TelephonyCallSession.Event.Type.IMS_CALL_STATE_CHANGED: 396 return "IMS_CALL_STATE_CHANGED"; 397 case TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED: 398 return "IMS_CALL_TERMINATED"; 399 case TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER: 400 return "IMS_CALL_HANDOVER"; 401 case TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER_FAILED: 402 return "IMS_CALL_HANDOVER_FAILED"; 403 case TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED: 404 return "PHONE_STATE_CHANGED"; 405 case TelephonyCallSession.Event.Type.NITZ_TIME: 406 return "NITZ_TIME"; 407 case TelephonyCallSession.Event.Type.AUDIO_CODEC: 408 return "AUDIO_CODEC"; 409 default: 410 return Integer.toString(event); 411 } 412 } 413 414 /** 415 * Convert the SMS session event into string 416 * @param event The event in integer 417 * @return The event in String 418 */ smsSessionEventToString(int event)419 private static String smsSessionEventToString(int event) { 420 switch (event) { 421 case SmsSession.Event.Type.EVENT_UNKNOWN: 422 return "EVENT_UNKNOWN"; 423 case SmsSession.Event.Type.SETTINGS_CHANGED: 424 return "SETTINGS_CHANGED"; 425 case SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED: 426 return "RIL_SERVICE_STATE_CHANGED"; 427 case SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED: 428 return "IMS_CONNECTION_STATE_CHANGED"; 429 case SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED: 430 return "IMS_CAPABILITIES_CHANGED"; 431 case SmsSession.Event.Type.DATA_CALL_LIST_CHANGED: 432 return "DATA_CALL_LIST_CHANGED"; 433 case SmsSession.Event.Type.SMS_SEND: 434 return "SMS_SEND"; 435 case SmsSession.Event.Type.SMS_SEND_RESULT: 436 return "SMS_SEND_RESULT"; 437 case SmsSession.Event.Type.SMS_RECEIVED: 438 return "SMS_RECEIVED"; 439 case SmsSession.Event.Type.INCOMPLETE_SMS_RECEIVED: 440 return "INCOMPLETE_SMS_RECEIVED"; 441 default: 442 return Integer.toString(event); 443 } 444 } 445 446 /** 447 * Print all metrics data for debugging purposes 448 * 449 * @param rawWriter Print writer 450 */ printAllMetrics(PrintWriter rawWriter)451 private synchronized void printAllMetrics(PrintWriter rawWriter) { 452 final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, " "); 453 454 pw.println("Telephony metrics proto:"); 455 pw.println("------------------------------------------"); 456 pw.println("Telephony events:"); 457 pw.increaseIndent(); 458 for (TelephonyEvent event : mTelephonyEvents) { 459 pw.print(event.timestampMillis); 460 pw.print(" ["); 461 pw.print(event.phoneId); 462 pw.print("] "); 463 464 pw.print("T="); 465 if (event.type == TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED) { 466 pw.print(telephonyEventToString(event.type) 467 + "(" + "Data RAT " + event.serviceState.dataRat 468 + " Voice RAT " + event.serviceState.voiceRat 469 + " Channel Number " + event.serviceState.channelNumber 470 + " NR Frequency Range " + event.serviceState.nrFrequencyRange 471 + " NR State " + event.serviceState.nrState 472 + ")"); 473 for (int i = 0; i < event.serviceState.networkRegistrationInfo.length; i++) { 474 pw.print("reg info: domain=" 475 + event.serviceState.networkRegistrationInfo[i].domain 476 + ", rat=" + event.serviceState.networkRegistrationInfo[i].rat); 477 } 478 } else { 479 pw.print(telephonyEventToString(event.type)); 480 } 481 482 pw.println(""); 483 } 484 485 pw.decreaseIndent(); 486 pw.println("Call sessions:"); 487 pw.increaseIndent(); 488 489 for (TelephonyCallSession callSession : mCompletedCallSessions) { 490 pw.print("Start time in minutes: " + callSession.startTimeMinutes); 491 pw.print(", phone: " + callSession.phoneId); 492 if (callSession.eventsDropped) { 493 pw.println(", events dropped: " + callSession.eventsDropped); 494 } else { 495 pw.println(""); 496 } 497 498 pw.println("Events: "); 499 pw.increaseIndent(); 500 for (TelephonyCallSession.Event event : callSession.events) { 501 pw.print(event.delay); 502 pw.print(" T="); 503 if (event.type == TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) { 504 pw.println(callSessionEventToString(event.type) 505 + "(" + "Data RAT " + event.serviceState.dataRat 506 + " Voice RAT " + event.serviceState.voiceRat 507 + " Channel Number " + event.serviceState.channelNumber 508 + " NR Frequency Range " + event.serviceState.nrFrequencyRange 509 + " NR State " + event.serviceState.nrState 510 + ")"); 511 } else if (event.type == TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED) { 512 pw.println(callSessionEventToString(event.type)); 513 pw.increaseIndent(); 514 for (RilCall call : event.calls) { 515 pw.println(call.index + ". Type = " + call.type + " State = " 516 + call.state + " End Reason " + call.callEndReason 517 + " Precise Disconnect Cause " + call.preciseDisconnectCause 518 + " isMultiparty = " + call.isMultiparty); 519 } 520 pw.decreaseIndent(); 521 } else if (event.type == TelephonyCallSession.Event.Type.AUDIO_CODEC) { 522 pw.println(callSessionEventToString(event.type) 523 + "(" + event.audioCodec + ")"); 524 } else { 525 pw.println(callSessionEventToString(event.type)); 526 } 527 } 528 pw.decreaseIndent(); 529 } 530 531 pw.decreaseIndent(); 532 pw.println("Sms sessions:"); 533 pw.increaseIndent(); 534 535 int count = 0; 536 for (SmsSession smsSession : mCompletedSmsSessions) { 537 count++; 538 pw.print("[" + count + "] Start time in minutes: " 539 + smsSession.startTimeMinutes); 540 pw.print(", phone: " + smsSession.phoneId); 541 if (smsSession.eventsDropped) { 542 pw.println(", events dropped: " + smsSession.eventsDropped); 543 } else { 544 pw.println(""); 545 } 546 pw.println("Events: "); 547 pw.increaseIndent(); 548 for (SmsSession.Event event : smsSession.events) { 549 pw.print(event.delay); 550 pw.print(" T="); 551 if (event.type == SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED) { 552 pw.println(smsSessionEventToString(event.type) 553 + "(" + "Data RAT " + event.serviceState.dataRat 554 + " Voice RAT " + event.serviceState.voiceRat 555 + " Channel Number " + event.serviceState.channelNumber 556 + " NR Frequency Range " + event.serviceState.nrFrequencyRange 557 + " NR State " + event.serviceState.nrState 558 + ")"); 559 } else if (event.type == SmsSession.Event.Type.SMS_RECEIVED) { 560 pw.println(smsSessionEventToString(event.type)); 561 pw.increaseIndent(); 562 switch (event.smsType) { 563 case SmsSession.Event.SmsType.SMS_TYPE_SMS_PP: 564 pw.println("Type: SMS-PP"); 565 break; 566 case SmsSession.Event.SmsType.SMS_TYPE_VOICEMAIL_INDICATION: 567 pw.println("Type: Voicemail indication"); 568 break; 569 case SmsSession.Event.SmsType.SMS_TYPE_ZERO: 570 pw.println("Type: zero"); 571 break; 572 case SmsSession.Event.SmsType.SMS_TYPE_WAP_PUSH: 573 pw.println("Type: WAP PUSH"); 574 break; 575 default: 576 break; 577 } 578 if (event.errorCode != SmsManager.RESULT_ERROR_NONE) { 579 pw.println("E=" + event.errorCode); 580 } 581 pw.decreaseIndent(); 582 } else if (event.type == SmsSession.Event.Type.SMS_SEND 583 || event.type == SmsSession.Event.Type.SMS_SEND_RESULT) { 584 pw.println(smsSessionEventToString(event.type)); 585 pw.increaseIndent(); 586 pw.println("ReqId=" + event.rilRequestId); 587 pw.println("E=" + event.errorCode); 588 pw.println("RilE=" + event.error); 589 pw.println("ImsE=" + event.imsError); 590 pw.decreaseIndent(); 591 } else if (event.type == SmsSession.Event.Type.INCOMPLETE_SMS_RECEIVED) { 592 pw.println(smsSessionEventToString(event.type)); 593 pw.increaseIndent(); 594 pw.println("Received: " + event.incompleteSms.receivedParts + "/" 595 + event.incompleteSms.totalParts); 596 pw.decreaseIndent(); 597 } 598 } 599 pw.decreaseIndent(); 600 } 601 602 pw.decreaseIndent(); 603 pw.println("Modem power stats:"); 604 pw.increaseIndent(); 605 606 BatteryStatsManager batteryStatsManager = mContext == null ? null : 607 (BatteryStatsManager) mContext.getSystemService(Context.BATTERY_STATS_SERVICE); 608 ModemPowerStats s = new ModemPowerMetrics(batteryStatsManager).buildProto(); 609 610 pw.println("Power log duration (battery time) (ms): " + s.loggingDurationMs); 611 pw.println("Energy consumed by modem (mAh): " + s.energyConsumedMah); 612 pw.println("Number of packets sent (tx): " + s.numPacketsTx); 613 pw.println("Number of bytes sent (tx): " + s.numBytesTx); 614 pw.println("Number of packets received (rx): " + s.numPacketsRx); 615 pw.println("Number of bytes received (rx): " + s.numBytesRx); 616 pw.println("Amount of time kernel is active because of cellular data (ms): " 617 + s.cellularKernelActiveTimeMs); 618 pw.println("Amount of time spent in very poor rx signal level (ms): " 619 + s.timeInVeryPoorRxSignalLevelMs); 620 pw.println("Amount of time modem is in sleep (ms): " + s.sleepTimeMs); 621 pw.println("Amount of time modem is in idle (ms): " + s.idleTimeMs); 622 pw.println("Amount of time modem is in rx (ms): " + s.rxTimeMs); 623 pw.println("Amount of time modem is in tx (ms): " + Arrays.toString(s.txTimeMs)); 624 pw.println("Amount of time phone spent in various Radio Access Technologies (ms): " 625 + Arrays.toString(s.timeInRatMs)); 626 pw.println("Amount of time phone spent in various cellular " 627 + "rx signal strength levels (ms): " 628 + Arrays.toString(s.timeInRxSignalStrengthLevelMs)); 629 pw.println("Energy consumed across measured modem rails (mAh): " 630 + new DecimalFormat("#.##").format(s.monitoredRailEnergyConsumedMah)); 631 pw.decreaseIndent(); 632 pw.println("Hardware Version: " + SystemProperties.get("ro.boot.revision", "")); 633 634 pw.decreaseIndent(); 635 pw.println("LinkBandwidthEstimator stats:"); 636 pw.increaseIndent(); 637 638 pw.println("Tx"); 639 for (BwEstimationStats stats : mBwEstStatsMapList.get(0).values()) { 640 pw.println(stats.toString()); 641 } 642 643 pw.println("Rx"); 644 for (BwEstimationStats stats : mBwEstStatsMapList.get(1).values()) { 645 pw.println(stats.toString()); 646 } 647 } 648 649 /** 650 * Convert the telephony proto into Base-64 encoded string 651 * 652 * @param proto Telephony proto 653 * @return Encoded string 654 */ convertProtoToBase64String(TelephonyLog proto)655 private static String convertProtoToBase64String(TelephonyLog proto) { 656 return Base64.encodeToString( 657 TelephonyProto.TelephonyLog.toByteArray(proto), Base64.DEFAULT); 658 } 659 660 /** 661 * Reset all events and sessions 662 */ reset()663 private synchronized void reset() { 664 mTelephonyEvents.clear(); 665 mCompletedCallSessions.clear(); 666 mCompletedSmsSessions.clear(); 667 mBwEstStatsMapList.get(0).clear(); 668 mBwEstStatsMapList.get(1).clear(); 669 670 mTelephonyEventsDropped = false; 671 672 mStartSystemTimeMs = System.currentTimeMillis(); 673 mStartElapsedTimeMs = SystemClock.elapsedRealtime(); 674 675 // Insert the last known sim state, enabled modem bitmap, active subscription info, 676 // service state, ims capabilities, ims connection states, carrier id and Data call 677 // events as the base. 678 // Sim state, modem bitmap and active subscription info events are logged before 679 // other events. 680 addTelephonyEvent(new TelephonyEventBuilder(mStartElapsedTimeMs, -1 /* phoneId */) 681 .setSimStateChange(mLastSimState).build()); 682 683 addTelephonyEvent(new TelephonyEventBuilder(mStartElapsedTimeMs, -1 /* phoneId */) 684 .setEnabledModemBitmap(mLastEnabledModemBitmap).build()); 685 686 for (int i = 0; i < mLastActiveSubscriptionInfos.size(); i++) { 687 final int key = mLastActiveSubscriptionInfos.keyAt(i); 688 TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) 689 .setActiveSubscriptionInfoChange(mLastActiveSubscriptionInfos.get(key)).build(); 690 addTelephonyEvent(event); 691 } 692 693 for (int i = 0; i < mLastServiceState.size(); i++) { 694 final int key = mLastServiceState.keyAt(i); 695 696 TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) 697 .setServiceState(mLastServiceState.get(key)).build(); 698 addTelephonyEvent(event); 699 } 700 701 for (int i = 0; i < mLastImsCapabilities.size(); i++) { 702 final int key = mLastImsCapabilities.keyAt(i); 703 704 TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) 705 .setImsCapabilities(mLastImsCapabilities.get(key)).build(); 706 addTelephonyEvent(event); 707 } 708 709 for (int i = 0; i < mLastImsConnectionState.size(); i++) { 710 final int key = mLastImsConnectionState.keyAt(i); 711 712 TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) 713 .setImsConnectionState(mLastImsConnectionState.get(key)).build(); 714 addTelephonyEvent(event); 715 } 716 717 for (int i = 0; i < mLastCarrierId.size(); i++) { 718 final int key = mLastCarrierId.keyAt(i); 719 TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) 720 .setCarrierIdMatching(mLastCarrierId.get(key)).build(); 721 addTelephonyEvent(event); 722 } 723 724 for (int i = 0; i < mLastNetworkCapabilitiesInfos.size(); i++) { 725 final int key = mLastNetworkCapabilitiesInfos.keyAt(i); 726 TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) 727 .setNetworkCapabilities(mLastNetworkCapabilitiesInfos.get(key)).build(); 728 addTelephonyEvent(event); 729 } 730 731 for (int i = 0; i < mLastRilDataCallEvents.size(); i++) { 732 final int key = mLastRilDataCallEvents.keyAt(i); 733 for (int j = 0; j < mLastRilDataCallEvents.get(key).size(); j++) { 734 final int cidKey = mLastRilDataCallEvents.get(key).keyAt(j); 735 RilDataCall[] dataCalls = new RilDataCall[1]; 736 dataCalls[0] = mLastRilDataCallEvents.get(key).get(cidKey); 737 addTelephonyEvent(new TelephonyEventBuilder(mStartElapsedTimeMs, key) 738 .setDataCalls(dataCalls).build()); 739 } 740 } 741 742 for (int i = 0; i < mLastRadioState.size(); i++) { 743 final int key = mLastRadioState.keyAt(i); 744 TelephonyEvent event = new TelephonyEventBuilder(mStartElapsedTimeMs, key) 745 .setRadioState(mLastRadioState.get(key)).build(); 746 addTelephonyEvent(event); 747 } 748 } 749 750 /** 751 * Build the telephony proto 752 * 753 * @return Telephony proto 754 */ buildProto()755 private synchronized TelephonyLog buildProto() { 756 757 TelephonyLog log = new TelephonyLog(); 758 // Build telephony events 759 log.events = new TelephonyEvent[mTelephonyEvents.size()]; 760 mTelephonyEvents.toArray(log.events); 761 log.eventsDropped = mTelephonyEventsDropped; 762 763 // Build call sessions 764 log.callSessions = new TelephonyCallSession[mCompletedCallSessions.size()]; 765 mCompletedCallSessions.toArray(log.callSessions); 766 767 // Build SMS sessions 768 log.smsSessions = new SmsSession[mCompletedSmsSessions.size()]; 769 mCompletedSmsSessions.toArray(log.smsSessions); 770 771 // Build histogram. Currently we only support RIL histograms. 772 List<TelephonyHistogram> rilHistograms = RIL.getTelephonyRILTimingHistograms(); 773 log.histograms = new TelephonyProto.TelephonyHistogram[rilHistograms.size()]; 774 for (int i = 0; i < rilHistograms.size(); i++) { 775 log.histograms[i] = new TelephonyProto.TelephonyHistogram(); 776 TelephonyHistogram rilHistogram = rilHistograms.get(i); 777 TelephonyProto.TelephonyHistogram histogramProto = log.histograms[i]; 778 779 histogramProto.category = rilHistogram.getCategory(); 780 histogramProto.id = rilHistogram.getId(); 781 histogramProto.minTimeMillis = rilHistogram.getMinTime(); 782 histogramProto.maxTimeMillis = rilHistogram.getMaxTime(); 783 histogramProto.avgTimeMillis = rilHistogram.getAverageTime(); 784 histogramProto.count = rilHistogram.getSampleCount(); 785 histogramProto.bucketCount = rilHistogram.getBucketCount(); 786 histogramProto.bucketEndPoints = rilHistogram.getBucketEndPoints(); 787 histogramProto.bucketCounters = rilHistogram.getBucketCounters(); 788 } 789 790 // Build modem power metrics 791 BatteryStatsManager batteryStatsManager = mContext == null ? null : 792 (BatteryStatsManager) mContext.getSystemService(Context.BATTERY_STATS_SERVICE); 793 log.modemPowerStats = new ModemPowerMetrics(batteryStatsManager).buildProto(); 794 795 // Log the hardware revision 796 log.hardwareRevision = SystemProperties.get("ro.boot.revision", ""); 797 798 // Log the starting system time 799 log.startTime = new TelephonyProto.Time(); 800 log.startTime.systemTimestampMillis = mStartSystemTimeMs; 801 log.startTime.elapsedTimestampMillis = mStartElapsedTimeMs; 802 803 log.endTime = new TelephonyProto.Time(); 804 log.endTime.systemTimestampMillis = System.currentTimeMillis(); 805 log.endTime.elapsedTimestampMillis = SystemClock.elapsedRealtime(); 806 807 // Log the last active subscription information. 808 int phoneCount = TelephonyManager.getDefault().getPhoneCount(); 809 ActiveSubscriptionInfo[] activeSubscriptionInfo = 810 new ActiveSubscriptionInfo[phoneCount]; 811 for (int i = 0; i < mLastActiveSubscriptionInfos.size(); i++) { 812 int key = mLastActiveSubscriptionInfos.keyAt(i); 813 activeSubscriptionInfo[key] = mLastActiveSubscriptionInfos.get(key); 814 } 815 for (int i = 0; i < phoneCount; i++) { 816 if (activeSubscriptionInfo[i] == null) { 817 activeSubscriptionInfo[i] = makeInvalidSubscriptionInfo(i); 818 } 819 } 820 log.lastActiveSubscriptionInfo = activeSubscriptionInfo; 821 log.bandwidthEstimatorStats = buildBandwidthEstimatorStats(); 822 return log; 823 } 824 825 /** Update the sim state. */ updateSimState(int phoneId, int simState)826 public void updateSimState(int phoneId, int simState) { 827 int state = mapSimStateToProto(simState); 828 Integer lastSimState = mLastSimState.get(phoneId); 829 if (lastSimState == null || !lastSimState.equals(state)) { 830 mLastSimState.put(phoneId, state); 831 addTelephonyEvent( 832 new TelephonyEventBuilder(phoneId).setSimStateChange(mLastSimState).build()); 833 } 834 } 835 836 /** Update active subscription info list. */ updateActiveSubscriptionInfoList(List<SubscriptionInfo> subInfos)837 public synchronized void updateActiveSubscriptionInfoList(List<SubscriptionInfo> subInfos) { 838 List<Integer> inActivePhoneList = new ArrayList<>(); 839 for (int i = 0; i < mLastActiveSubscriptionInfos.size(); i++) { 840 inActivePhoneList.add(mLastActiveSubscriptionInfos.keyAt(i)); 841 } 842 843 for (SubscriptionInfo info : subInfos) { 844 int phoneId = info.getSimSlotIndex(); 845 inActivePhoneList.removeIf(value -> value.equals(phoneId)); 846 ActiveSubscriptionInfo activeSubscriptionInfo = new ActiveSubscriptionInfo(); 847 activeSubscriptionInfo.slotIndex = phoneId; 848 activeSubscriptionInfo.isOpportunistic = info.isOpportunistic() ? 1 : 0; 849 activeSubscriptionInfo.carrierId = info.getCarrierId(); 850 if (info.getMccString() != null && info.getMncString() != null) { 851 activeSubscriptionInfo.simMccmnc = info.getMccString() + info.getMncString(); 852 } 853 if (!MessageNano.messageNanoEquals( 854 mLastActiveSubscriptionInfos.get(phoneId), activeSubscriptionInfo)) { 855 addTelephonyEvent(new TelephonyEventBuilder(phoneId) 856 .setActiveSubscriptionInfoChange(activeSubscriptionInfo).build()); 857 858 mLastActiveSubscriptionInfos.put(phoneId, activeSubscriptionInfo); 859 } 860 } 861 862 for (int phoneId : inActivePhoneList) { 863 mLastActiveSubscriptionInfos.remove(phoneId); 864 addTelephonyEvent(new TelephonyEventBuilder(phoneId) 865 .setActiveSubscriptionInfoChange(makeInvalidSubscriptionInfo(phoneId)).build()); 866 } 867 } 868 869 /** Update the enabled modem bitmap. */ updateEnabledModemBitmap(int enabledModemBitmap)870 public void updateEnabledModemBitmap(int enabledModemBitmap) { 871 if (mLastEnabledModemBitmap == enabledModemBitmap) return; 872 mLastEnabledModemBitmap = enabledModemBitmap; 873 addTelephonyEvent(new TelephonyEventBuilder() 874 .setEnabledModemBitmap(mLastEnabledModemBitmap).build()); 875 } 876 makeInvalidSubscriptionInfo(int phoneId)877 private static ActiveSubscriptionInfo makeInvalidSubscriptionInfo(int phoneId) { 878 ActiveSubscriptionInfo invalidSubscriptionInfo = new ActiveSubscriptionInfo(); 879 invalidSubscriptionInfo.slotIndex = phoneId; 880 invalidSubscriptionInfo.carrierId = -1; 881 invalidSubscriptionInfo.isOpportunistic = -1; 882 return invalidSubscriptionInfo; 883 } 884 885 /** 886 * Reduce precision to meet privacy requirements. 887 * 888 * @param timestamp timestamp in milliseconds 889 * @return Precision reduced timestamp in minutes 890 */ roundSessionStart(long timestamp)891 static int roundSessionStart(long timestamp) { 892 return (int) ((timestamp) / (MINUTE_IN_MILLIS * SESSION_START_PRECISION_MINUTES) 893 * (SESSION_START_PRECISION_MINUTES)); 894 } 895 896 /** 897 * Write the Carrier Key change event 898 * 899 * @param phoneId Phone id 900 * @param keyType type of key 901 * @param isDownloadSuccessful true if the key was successfully downloaded 902 */ writeCarrierKeyEvent(int phoneId, int keyType, boolean isDownloadSuccessful)903 public void writeCarrierKeyEvent(int phoneId, int keyType, boolean isDownloadSuccessful) { 904 final CarrierKeyChange carrierKeyChange = new CarrierKeyChange(); 905 carrierKeyChange.keyType = keyType; 906 carrierKeyChange.isDownloadSuccessful = isDownloadSuccessful; 907 TelephonyEvent event = new TelephonyEventBuilder(phoneId).setCarrierKeyChange( 908 carrierKeyChange).build(); 909 addTelephonyEvent(event); 910 } 911 912 913 /** 914 * Get the time interval with reduced prevision 915 * 916 * @param previousTimestamp Previous timestamp in milliseconds 917 * @param currentTimestamp Current timestamp in milliseconds 918 * @return The time interval 919 */ toPrivacyFuzzedTimeInterval(long previousTimestamp, long currentTimestamp)920 static int toPrivacyFuzzedTimeInterval(long previousTimestamp, long currentTimestamp) { 921 long diff = currentTimestamp - previousTimestamp; 922 if (diff < 0) { 923 return TimeInterval.TI_UNKNOWN; 924 } else if (diff <= 10) { 925 return TimeInterval.TI_10_MILLIS; 926 } else if (diff <= 20) { 927 return TimeInterval.TI_20_MILLIS; 928 } else if (diff <= 50) { 929 return TimeInterval.TI_50_MILLIS; 930 } else if (diff <= 100) { 931 return TimeInterval.TI_100_MILLIS; 932 } else if (diff <= 200) { 933 return TimeInterval.TI_200_MILLIS; 934 } else if (diff <= 500) { 935 return TimeInterval.TI_500_MILLIS; 936 } else if (diff <= 1000) { 937 return TimeInterval.TI_1_SEC; 938 } else if (diff <= 2000) { 939 return TimeInterval.TI_2_SEC; 940 } else if (diff <= 5000) { 941 return TimeInterval.TI_5_SEC; 942 } else if (diff <= 10000) { 943 return TimeInterval.TI_10_SEC; 944 } else if (diff <= 30000) { 945 return TimeInterval.TI_30_SEC; 946 } else if (diff <= 60000) { 947 return TimeInterval.TI_1_MINUTE; 948 } else if (diff <= 180000) { 949 return TimeInterval.TI_3_MINUTES; 950 } else if (diff <= 600000) { 951 return TimeInterval.TI_10_MINUTES; 952 } else if (diff <= 1800000) { 953 return TimeInterval.TI_30_MINUTES; 954 } else if (diff <= 3600000) { 955 return TimeInterval.TI_1_HOUR; 956 } else if (diff <= 7200000) { 957 return TimeInterval.TI_2_HOURS; 958 } else if (diff <= 14400000) { 959 return TimeInterval.TI_4_HOURS; 960 } else { 961 return TimeInterval.TI_MANY_HOURS; 962 } 963 } 964 965 /** 966 * Convert the service state into service state proto 967 * 968 * @param serviceState Service state 969 * @return Service state proto 970 */ toServiceStateProto(ServiceState serviceState)971 private TelephonyServiceState toServiceStateProto(ServiceState serviceState) { 972 TelephonyServiceState ssProto = new TelephonyServiceState(); 973 974 ssProto.voiceRoamingType = serviceState.getVoiceRoamingType(); 975 ssProto.dataRoamingType = serviceState.getDataRoamingType(); 976 977 ssProto.voiceOperator = new TelephonyServiceState.TelephonyOperator(); 978 ssProto.dataOperator = new TelephonyServiceState.TelephonyOperator(); 979 if (serviceState.getOperatorAlphaLong() != null) { 980 ssProto.voiceOperator.alphaLong = serviceState.getOperatorAlphaLong(); 981 ssProto.dataOperator.alphaLong = serviceState.getOperatorAlphaLong(); 982 } 983 984 if (serviceState.getOperatorAlphaShort() != null) { 985 ssProto.voiceOperator.alphaShort = serviceState.getOperatorAlphaShort(); 986 ssProto.dataOperator.alphaShort = serviceState.getOperatorAlphaShort(); 987 } 988 989 if (serviceState.getOperatorNumeric() != null) { 990 ssProto.voiceOperator.numeric = serviceState.getOperatorNumeric(); 991 ssProto.dataOperator.numeric = serviceState.getOperatorNumeric(); 992 } 993 994 // Log PS WWAN only because CS WWAN would be exactly the same as voiceRat, and PS WLAN 995 // would be always IWLAN in the rat field. 996 // Note that we intentionally do not log reg state because it changes too frequently that 997 // will grow the proto size too much. 998 List<TelephonyServiceState.NetworkRegistrationInfo> nriList = new ArrayList<>(); 999 NetworkRegistrationInfo nri = serviceState.getNetworkRegistrationInfo( 1000 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 1001 if (nri != null) { 1002 TelephonyServiceState.NetworkRegistrationInfo nriProto = 1003 new TelephonyServiceState.NetworkRegistrationInfo(); 1004 nriProto.domain = TelephonyServiceState.Domain.DOMAIN_PS; 1005 nriProto.transport = TelephonyServiceState.Transport.TRANSPORT_WWAN; 1006 nriProto.rat = ServiceState.networkTypeToRilRadioTechnology( 1007 nri.getAccessNetworkTechnology()); 1008 nriList.add(nriProto); 1009 ssProto.networkRegistrationInfo = 1010 new TelephonyServiceState.NetworkRegistrationInfo[nriList.size()]; 1011 nriList.toArray(ssProto.networkRegistrationInfo); 1012 } 1013 1014 ssProto.voiceRat = serviceState.getRilVoiceRadioTechnology(); 1015 ssProto.dataRat = serviceState.getRilDataRadioTechnology(); 1016 ssProto.channelNumber = serviceState.getChannelNumber(); 1017 ssProto.nrFrequencyRange = serviceState.getNrFrequencyRange(); 1018 ssProto.nrState = serviceState.getNrState(); 1019 return ssProto; 1020 } 1021 1022 /** 1023 * Annotate the call session with events 1024 * 1025 * @param timestamp Event timestamp 1026 * @param phoneId Phone id 1027 * @param eventBuilder Call session event builder 1028 */ annotateInProgressCallSession(long timestamp, int phoneId, CallSessionEventBuilder eventBuilder)1029 private synchronized void annotateInProgressCallSession(long timestamp, int phoneId, 1030 CallSessionEventBuilder eventBuilder) { 1031 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 1032 if (callSession != null) { 1033 callSession.addEvent(timestamp, eventBuilder); 1034 } 1035 } 1036 1037 /** 1038 * Annotate the SMS session with events 1039 * 1040 * @param timestamp Event timestamp 1041 * @param phoneId Phone id 1042 * @param eventBuilder SMS session event builder 1043 */ annotateInProgressSmsSession(long timestamp, int phoneId, SmsSessionEventBuilder eventBuilder)1044 private synchronized void annotateInProgressSmsSession(long timestamp, int phoneId, 1045 SmsSessionEventBuilder eventBuilder) { 1046 InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId); 1047 if (smsSession != null) { 1048 smsSession.addEvent(timestamp, eventBuilder); 1049 } 1050 } 1051 1052 /** 1053 * Create the call session if there isn't any existing one 1054 * 1055 * @param phoneId Phone id 1056 * @return The call session 1057 */ startNewCallSessionIfNeeded(int phoneId)1058 private synchronized InProgressCallSession startNewCallSessionIfNeeded(int phoneId) { 1059 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 1060 if (callSession == null) { 1061 logv("Starting a new call session on phone " + phoneId); 1062 callSession = new InProgressCallSession(phoneId); 1063 mInProgressCallSessions.append(phoneId, callSession); 1064 1065 // Insert the latest service state, ims capabilities, and ims connection states as the 1066 // base. 1067 TelephonyServiceState serviceState = mLastServiceState.get(phoneId); 1068 if (serviceState != null) { 1069 callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder( 1070 TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) 1071 .setServiceState(serviceState)); 1072 } 1073 1074 ImsCapabilities imsCapabilities = mLastImsCapabilities.get(phoneId); 1075 if (imsCapabilities != null) { 1076 callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder( 1077 TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED) 1078 .setImsCapabilities(imsCapabilities)); 1079 } 1080 1081 ImsConnectionState imsConnectionState = mLastImsConnectionState.get(phoneId); 1082 if (imsConnectionState != null) { 1083 callSession.addEvent(callSession.startElapsedTimeMs, new CallSessionEventBuilder( 1084 TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED) 1085 .setImsConnectionState(imsConnectionState)); 1086 } 1087 } 1088 return callSession; 1089 } 1090 1091 /** 1092 * Create the SMS session if there isn't any existing one 1093 * 1094 * @param phoneId Phone id 1095 * @return The SMS session 1096 */ startNewSmsSessionIfNeeded(int phoneId)1097 private synchronized InProgressSmsSession startNewSmsSessionIfNeeded(int phoneId) { 1098 InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId); 1099 if (smsSession == null) { 1100 logv("Starting a new sms session on phone " + phoneId); 1101 smsSession = startNewSmsSession(phoneId); 1102 mInProgressSmsSessions.append(phoneId, smsSession); 1103 } 1104 return smsSession; 1105 } 1106 1107 /** 1108 * Create a new SMS session 1109 * 1110 * @param phoneId Phone id 1111 * @return The SMS session 1112 */ startNewSmsSession(int phoneId)1113 private InProgressSmsSession startNewSmsSession(int phoneId) { 1114 InProgressSmsSession smsSession = new InProgressSmsSession(phoneId); 1115 1116 // Insert the latest service state, ims capabilities, and ims connection state as the 1117 // base. 1118 TelephonyServiceState serviceState = mLastServiceState.get(phoneId); 1119 if (serviceState != null) { 1120 smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder( 1121 SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED) 1122 .setServiceState(serviceState)); 1123 } 1124 1125 ImsCapabilities imsCapabilities = mLastImsCapabilities.get(phoneId); 1126 if (imsCapabilities != null) { 1127 smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder( 1128 SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED) 1129 .setImsCapabilities(imsCapabilities)); 1130 } 1131 1132 ImsConnectionState imsConnectionState = mLastImsConnectionState.get(phoneId); 1133 if (imsConnectionState != null) { 1134 smsSession.addEvent(smsSession.startElapsedTimeMs, new SmsSessionEventBuilder( 1135 SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED) 1136 .setImsConnectionState(imsConnectionState)); 1137 } 1138 return smsSession; 1139 } 1140 1141 /** 1142 * Finish the call session and move it into the completed session 1143 * 1144 * @param inProgressCallSession The in progress call session 1145 */ finishCallSession(InProgressCallSession inProgressCallSession)1146 private synchronized void finishCallSession(InProgressCallSession inProgressCallSession) { 1147 TelephonyCallSession callSession = new TelephonyCallSession(); 1148 callSession.events = new TelephonyCallSession.Event[inProgressCallSession.events.size()]; 1149 inProgressCallSession.events.toArray(callSession.events); 1150 callSession.startTimeMinutes = inProgressCallSession.startSystemTimeMin; 1151 callSession.phoneId = inProgressCallSession.phoneId; 1152 callSession.eventsDropped = inProgressCallSession.isEventsDropped(); 1153 if (mCompletedCallSessions.size() >= MAX_COMPLETED_CALL_SESSIONS) { 1154 mCompletedCallSessions.removeFirst(); 1155 } 1156 mCompletedCallSessions.add(callSession); 1157 mInProgressCallSessions.remove(inProgressCallSession.phoneId); 1158 logv("Call session finished"); 1159 } 1160 1161 /** 1162 * Finish the SMS session and move it into the completed session 1163 * 1164 * @param inProgressSmsSession The in progress SMS session 1165 */ finishSmsSessionIfNeeded(InProgressSmsSession inProgressSmsSession)1166 private synchronized void finishSmsSessionIfNeeded(InProgressSmsSession inProgressSmsSession) { 1167 if (inProgressSmsSession.getNumExpectedResponses() == 0) { 1168 SmsSession smsSession = finishSmsSession(inProgressSmsSession); 1169 1170 mInProgressSmsSessions.remove(inProgressSmsSession.phoneId); 1171 logv("SMS session finished"); 1172 } 1173 } 1174 finishSmsSession(InProgressSmsSession inProgressSmsSession)1175 private synchronized SmsSession finishSmsSession(InProgressSmsSession inProgressSmsSession) { 1176 SmsSession smsSession = new SmsSession(); 1177 smsSession.events = new SmsSession.Event[inProgressSmsSession.events.size()]; 1178 inProgressSmsSession.events.toArray(smsSession.events); 1179 smsSession.startTimeMinutes = inProgressSmsSession.startSystemTimeMin; 1180 smsSession.phoneId = inProgressSmsSession.phoneId; 1181 smsSession.eventsDropped = inProgressSmsSession.isEventsDropped(); 1182 1183 if (mCompletedSmsSessions.size() >= MAX_COMPLETED_SMS_SESSIONS) { 1184 mCompletedSmsSessions.removeFirst(); 1185 } 1186 mCompletedSmsSessions.add(smsSession); 1187 return smsSession; 1188 } 1189 1190 /** 1191 * Add telephony event into the queue 1192 * 1193 * @param event Telephony event 1194 */ addTelephonyEvent(TelephonyEvent event)1195 private synchronized void addTelephonyEvent(TelephonyEvent event) { 1196 if (mTelephonyEvents.size() >= MAX_TELEPHONY_EVENTS) { 1197 mTelephonyEvents.removeFirst(); 1198 mTelephonyEventsDropped = true; 1199 } 1200 mTelephonyEvents.add(event); 1201 } 1202 1203 /** 1204 * Write service changed event 1205 * 1206 * @param phoneId Phone id 1207 * @param serviceState Service state 1208 */ writeServiceStateChanged(int phoneId, ServiceState serviceState)1209 public synchronized void writeServiceStateChanged(int phoneId, ServiceState serviceState) { 1210 1211 TelephonyEvent event = new TelephonyEventBuilder(phoneId) 1212 .setServiceState(toServiceStateProto(serviceState)).build(); 1213 1214 // If service state doesn't change, we don't log the event. 1215 if (mLastServiceState.get(phoneId) != null && 1216 Arrays.equals(TelephonyServiceState.toByteArray(mLastServiceState.get(phoneId)), 1217 TelephonyServiceState.toByteArray(event.serviceState))) { 1218 return; 1219 } 1220 1221 mLastServiceState.put(phoneId, event.serviceState); 1222 addTelephonyEvent(event); 1223 1224 annotateInProgressCallSession(event.timestampMillis, phoneId, 1225 new CallSessionEventBuilder( 1226 TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) 1227 .setServiceState(event.serviceState)); 1228 annotateInProgressSmsSession(event.timestampMillis, phoneId, 1229 new SmsSessionEventBuilder( 1230 SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED) 1231 .setServiceState(event.serviceState)); 1232 } 1233 1234 /** 1235 * Write data stall event 1236 * 1237 * @param phoneId Phone id 1238 * @param recoveryAction Data stall recovery action 1239 */ writeDataStallEvent(int phoneId, int recoveryAction)1240 public void writeDataStallEvent(int phoneId, int recoveryAction) { 1241 addTelephonyEvent(new TelephonyEventBuilder(phoneId) 1242 .setDataStallRecoveryAction(recoveryAction).build()); 1243 } 1244 1245 /** 1246 * Write SignalStrength event 1247 * 1248 * @param phoneId Phone id 1249 * @param signalStrength Signal strength at the time of data stall recovery 1250 */ writeSignalStrengthEvent(int phoneId, int signalStrength)1251 public void writeSignalStrengthEvent(int phoneId, int signalStrength) { 1252 addTelephonyEvent(new TelephonyEventBuilder(phoneId) 1253 .setSignalStrength(signalStrength).build()); 1254 } 1255 cloneCurrentTelephonySettings(int phoneId)1256 private TelephonySettings cloneCurrentTelephonySettings(int phoneId) { 1257 TelephonySettings newSettings = new TelephonySettings(); 1258 TelephonySettings lastSettings = mLastSettings.get(phoneId); 1259 if (lastSettings != null) { 1260 // No clone method available, so each relevant field is copied individually. 1261 newSettings.preferredNetworkMode = lastSettings.preferredNetworkMode; 1262 newSettings.isEnhanced4GLteModeEnabled = lastSettings.isEnhanced4GLteModeEnabled; 1263 newSettings.isVtOverLteEnabled = lastSettings.isVtOverLteEnabled; 1264 newSettings.isWifiCallingEnabled = lastSettings.isWifiCallingEnabled; 1265 newSettings.isVtOverWifiEnabled = lastSettings.isVtOverWifiEnabled; 1266 } 1267 return newSettings; 1268 } 1269 1270 /** 1271 * Write IMS feature settings changed event 1272 * 1273 * @param phoneId Phone id 1274 * @param feature IMS feature 1275 * @param network The IMS network type 1276 * @param value The settings. 0 indicates disabled, otherwise enabled. 1277 */ writeImsSetFeatureValue(int phoneId, int feature, int network, int value)1278 public synchronized void writeImsSetFeatureValue(int phoneId, int feature, int network, 1279 int value) { 1280 TelephonySettings s = cloneCurrentTelephonySettings(phoneId); 1281 if (network == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { 1282 switch (feature) { 1283 case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: 1284 s.isEnhanced4GLteModeEnabled = (value != 0); 1285 break; 1286 case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: 1287 s.isVtOverLteEnabled = (value != 0); 1288 break; 1289 } 1290 } else if (network == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) { 1291 switch (feature) { 1292 case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE: 1293 s.isWifiCallingEnabled = (value != 0); 1294 break; 1295 case MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO: 1296 s.isVtOverWifiEnabled = (value != 0); 1297 break; 1298 } 1299 } 1300 1301 // If the settings don't change, we don't log the event. 1302 if (mLastSettings.get(phoneId) != null && 1303 Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)), 1304 TelephonySettings.toByteArray(s))) { 1305 return; 1306 } 1307 1308 mLastSettings.put(phoneId, s); 1309 1310 TelephonyEvent event = new TelephonyEventBuilder(phoneId).setSettings(s).build(); 1311 addTelephonyEvent(event); 1312 1313 annotateInProgressCallSession(event.timestampMillis, phoneId, 1314 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.SETTINGS_CHANGED) 1315 .setSettings(s)); 1316 annotateInProgressSmsSession(event.timestampMillis, phoneId, 1317 new SmsSessionEventBuilder(SmsSession.Event.Type.SETTINGS_CHANGED) 1318 .setSettings(s)); 1319 } 1320 1321 /** 1322 * Write the preferred network settings changed event 1323 * 1324 * @param phoneId Phone id 1325 * @param networkType The preferred network 1326 */ writeSetPreferredNetworkType(int phoneId, @PrefNetworkMode int networkType)1327 public synchronized void writeSetPreferredNetworkType(int phoneId, 1328 @PrefNetworkMode int networkType) { 1329 TelephonySettings s = cloneCurrentTelephonySettings(phoneId); 1330 s.preferredNetworkMode = networkType + 1; 1331 1332 // If the settings don't change, we don't log the event. 1333 if (mLastSettings.get(phoneId) != null && 1334 Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)), 1335 TelephonySettings.toByteArray(s))) { 1336 return; 1337 } 1338 1339 mLastSettings.put(phoneId, s); 1340 1341 addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSettings(s).build()); 1342 } 1343 1344 /** 1345 * Write the IMS connection state changed event 1346 * 1347 * @param phoneId Phone id 1348 * @param state IMS connection state 1349 * @param reasonInfo The reason info. Only used for disconnected state. 1350 */ writeOnImsConnectionState(int phoneId, int state, ImsReasonInfo reasonInfo)1351 public synchronized void writeOnImsConnectionState(int phoneId, int state, 1352 ImsReasonInfo reasonInfo) { 1353 ImsConnectionState imsState = new ImsConnectionState(); 1354 imsState.state = state; 1355 1356 if (reasonInfo != null) { 1357 TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo(); 1358 1359 ri.reasonCode = reasonInfo.getCode(); 1360 ri.extraCode = reasonInfo.getExtraCode(); 1361 String extraMessage = reasonInfo.getExtraMessage(); 1362 if (extraMessage != null) { 1363 ri.extraMessage = extraMessage; 1364 } 1365 1366 imsState.reasonInfo = ri; 1367 } 1368 1369 // If the connection state does not change, do not log it. 1370 if (mLastImsConnectionState.get(phoneId) != null && 1371 Arrays.equals(ImsConnectionState.toByteArray(mLastImsConnectionState.get(phoneId)), 1372 ImsConnectionState.toByteArray(imsState))) { 1373 return; 1374 } 1375 1376 mLastImsConnectionState.put(phoneId, imsState); 1377 1378 TelephonyEvent event = new TelephonyEventBuilder(phoneId) 1379 .setImsConnectionState(imsState).build(); 1380 addTelephonyEvent(event); 1381 1382 annotateInProgressCallSession(event.timestampMillis, phoneId, 1383 new CallSessionEventBuilder( 1384 TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED) 1385 .setImsConnectionState(event.imsConnectionState)); 1386 annotateInProgressSmsSession(event.timestampMillis, phoneId, 1387 new SmsSessionEventBuilder( 1388 SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED) 1389 .setImsConnectionState(event.imsConnectionState)); 1390 } 1391 1392 /** 1393 * Write the IMS capabilities changed event 1394 * 1395 * @param phoneId Phone id 1396 * @param capabilities IMS capabilities array 1397 */ writeOnImsCapabilities(int phoneId, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech, MmTelFeature.MmTelCapabilities capabilities)1398 public synchronized void writeOnImsCapabilities(int phoneId, 1399 @ImsRegistrationImplBase.ImsRegistrationTech int radioTech, 1400 MmTelFeature.MmTelCapabilities capabilities) { 1401 ImsCapabilities cap = new ImsCapabilities(); 1402 1403 if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) { 1404 cap.voiceOverLte = capabilities.isCapable( 1405 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); 1406 cap.videoOverLte = capabilities.isCapable( 1407 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO); 1408 cap.utOverLte = capabilities.isCapable( 1409 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT); 1410 1411 } else if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) { 1412 cap.voiceOverWifi = capabilities.isCapable( 1413 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); 1414 cap.videoOverWifi = capabilities.isCapable( 1415 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO); 1416 cap.utOverWifi = capabilities.isCapable( 1417 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT); 1418 } 1419 1420 TelephonyEvent event = new TelephonyEventBuilder(phoneId).setImsCapabilities(cap).build(); 1421 1422 // If the capabilities don't change, we don't log the event. 1423 if (mLastImsCapabilities.get(phoneId) != null && 1424 Arrays.equals(ImsCapabilities.toByteArray(mLastImsCapabilities.get(phoneId)), 1425 ImsCapabilities.toByteArray(cap))) { 1426 return; 1427 } 1428 1429 mLastImsCapabilities.put(phoneId, cap); 1430 addTelephonyEvent(event); 1431 1432 annotateInProgressCallSession(event.timestampMillis, phoneId, 1433 new CallSessionEventBuilder( 1434 TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED) 1435 .setImsCapabilities(event.imsCapabilities)); 1436 annotateInProgressSmsSession(event.timestampMillis, phoneId, 1437 new SmsSessionEventBuilder( 1438 SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED) 1439 .setImsCapabilities(event.imsCapabilities)); 1440 } 1441 1442 /** 1443 * Convert PDP type into the enumeration 1444 * 1445 * @param type PDP type 1446 * @return The proto defined enumeration 1447 */ toPdpType(String type)1448 private int toPdpType(String type) { 1449 switch (type) { 1450 case "IP": 1451 return PDP_TYPE_IP; 1452 case "IPV6": 1453 return PDP_TYPE_IPV6; 1454 case "IPV4V6": 1455 return PDP_TYPE_IPV4V6; 1456 case "PPP": 1457 return PDP_TYPE_PPP; 1458 case "NON-IP": 1459 return PDP_TYPE_NON_IP; 1460 case "UNSTRUCTURED": 1461 return PDP_TYPE_UNSTRUCTURED; 1462 } 1463 Rlog.e(TAG, "Unknown type: " + type); 1464 return PDP_UNKNOWN; 1465 } 1466 1467 /** 1468 * Write setup data call event 1469 * 1470 * @param phoneId Phone id 1471 * @param radioTechnology The data call RAT 1472 * @param profileId Data profile id 1473 * @param apn APN in string 1474 * @param protocol Data connection protocol 1475 */ writeSetupDataCall(int phoneId, int radioTechnology, int profileId, String apn, int protocol)1476 public void writeSetupDataCall(int phoneId, int radioTechnology, int profileId, String apn, 1477 int protocol) { 1478 1479 RilSetupDataCall setupDataCall = new RilSetupDataCall(); 1480 setupDataCall.rat = radioTechnology; 1481 setupDataCall.dataProfile = profileId + 1; // off by 1 between proto and RIL constants. 1482 if (apn != null) { 1483 setupDataCall.apn = apn; 1484 } 1485 1486 setupDataCall.type = protocol + 1; 1487 1488 addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSetupDataCall( 1489 setupDataCall).build()); 1490 } 1491 1492 /** 1493 * Write data call deactivate event 1494 * 1495 * @param phoneId Phone id 1496 * @param rilSerial RIL request serial number 1497 * @param cid call id 1498 * @param reason Deactivate reason 1499 */ writeRilDeactivateDataCall(int phoneId, int rilSerial, int cid, int reason)1500 public void writeRilDeactivateDataCall(int phoneId, int rilSerial, int cid, int reason) { 1501 1502 RilDeactivateDataCall deactivateDataCall = new RilDeactivateDataCall(); 1503 deactivateDataCall.cid = cid; 1504 switch (reason) { 1505 case DataService.REQUEST_REASON_NORMAL: 1506 deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_NONE; 1507 break; 1508 case DataService.REQUEST_REASON_SHUTDOWN: 1509 deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_RADIO_OFF; 1510 break; 1511 case DataService.REQUEST_REASON_HANDOVER: 1512 deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_HANDOVER; 1513 break; 1514 default: 1515 deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_UNKNOWN; 1516 } 1517 1518 addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDeactivateDataCall( 1519 deactivateDataCall).build()); 1520 } 1521 1522 /** 1523 * Write data call list event when connected 1524 * @param phoneId Phone id 1525 * @param cid Context Id, uniquely identifies the call 1526 * @param apnTypeBitmask Bitmask of supported APN types 1527 * @param state State of the data call event 1528 */ writeRilDataCallEvent(int phoneId, int cid, int apnTypeBitmask, int state)1529 public void writeRilDataCallEvent(int phoneId, int cid, 1530 int apnTypeBitmask, int state) { 1531 RilDataCall[] dataCalls = new RilDataCall[1]; 1532 dataCalls[0] = new RilDataCall(); 1533 dataCalls[0].cid = cid; 1534 dataCalls[0].apnTypeBitmask = apnTypeBitmask; 1535 dataCalls[0].state = state; 1536 1537 SparseArray<RilDataCall> dataCallList; 1538 if (mLastRilDataCallEvents.get(phoneId) != null) { 1539 // If the Data call event does not change, do not log it. 1540 if (mLastRilDataCallEvents.get(phoneId).get(cid) != null 1541 && Arrays.equals( 1542 RilDataCall.toByteArray(mLastRilDataCallEvents.get(phoneId).get(cid)), 1543 RilDataCall.toByteArray(dataCalls[0]))) { 1544 return; 1545 } 1546 dataCallList = mLastRilDataCallEvents.get(phoneId); 1547 } else { 1548 dataCallList = new SparseArray<>(); 1549 } 1550 1551 dataCallList.put(cid, dataCalls[0]); 1552 mLastRilDataCallEvents.put(phoneId, dataCallList); 1553 addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDataCalls(dataCalls).build()); 1554 } 1555 1556 /** 1557 * Write CS call list event 1558 * 1559 * @param phoneId Phone id 1560 * @param connections Array of GsmCdmaConnection objects 1561 */ writeRilCallList(int phoneId, ArrayList<GsmCdmaConnection> connections, String countryIso)1562 public void writeRilCallList(int phoneId, ArrayList<GsmCdmaConnection> connections, 1563 String countryIso) { 1564 logv("Logging CallList Changed Connections Size = " + connections.size()); 1565 InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId); 1566 if (callSession == null) { 1567 Rlog.e(TAG, "writeRilCallList: Call session is missing"); 1568 } else { 1569 RilCall[] calls = convertConnectionsToRilCalls(connections, countryIso); 1570 callSession.addEvent( 1571 new CallSessionEventBuilder( 1572 TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED) 1573 .setRilCalls(calls) 1574 ); 1575 logv("Logged Call list changed"); 1576 if (callSession.isPhoneIdle() && disconnectReasonsKnown(calls)) { 1577 finishCallSession(callSession); 1578 } 1579 } 1580 } 1581 disconnectReasonsKnown(RilCall[] calls)1582 private boolean disconnectReasonsKnown(RilCall[] calls) { 1583 for (RilCall call : calls) { 1584 if (call.callEndReason == 0) return false; 1585 } 1586 return true; 1587 } 1588 convertConnectionsToRilCalls(ArrayList<GsmCdmaConnection> mConnections, String countryIso)1589 private RilCall[] convertConnectionsToRilCalls(ArrayList<GsmCdmaConnection> mConnections, 1590 String countryIso) { 1591 RilCall[] calls = new RilCall[mConnections.size()]; 1592 for (int i = 0; i < mConnections.size(); i++) { 1593 calls[i] = new RilCall(); 1594 calls[i].index = i; 1595 convertConnectionToRilCall(mConnections.get(i), calls[i], countryIso); 1596 } 1597 return calls; 1598 } 1599 convertEmergencyNumberToEmergencyNumberInfo(EmergencyNumber num)1600 private EmergencyNumberInfo convertEmergencyNumberToEmergencyNumberInfo(EmergencyNumber num) { 1601 EmergencyNumberInfo emergencyNumberInfo = new EmergencyNumberInfo(); 1602 emergencyNumberInfo.address = num.getNumber(); 1603 emergencyNumberInfo.countryIso = num.getCountryIso(); 1604 emergencyNumberInfo.mnc = num.getMnc(); 1605 emergencyNumberInfo.serviceCategoriesBitmask = num.getEmergencyServiceCategoryBitmask(); 1606 emergencyNumberInfo.urns = num.getEmergencyUrns().stream().toArray(String[]::new); 1607 emergencyNumberInfo.numberSourcesBitmask = num.getEmergencyNumberSourceBitmask(); 1608 emergencyNumberInfo.routing = num.getEmergencyCallRouting(); 1609 return emergencyNumberInfo; 1610 } 1611 convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call, String countryIso)1612 private void convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call, 1613 String countryIso) { 1614 if (conn.isIncoming()) { 1615 call.type = Type.MT; 1616 } else { 1617 call.type = Type.MO; 1618 } 1619 switch (conn.getState()) { 1620 case IDLE: 1621 call.state = CallState.CALL_IDLE; 1622 break; 1623 case ACTIVE: 1624 call.state = CallState.CALL_ACTIVE; 1625 break; 1626 case HOLDING: 1627 call.state = CallState.CALL_HOLDING; 1628 break; 1629 case DIALING: 1630 call.state = CallState.CALL_DIALING; 1631 break; 1632 case ALERTING: 1633 call.state = CallState.CALL_ALERTING; 1634 break; 1635 case INCOMING: 1636 call.state = CallState.CALL_INCOMING; 1637 break; 1638 case WAITING: 1639 call.state = CallState.CALL_WAITING; 1640 break; 1641 case DISCONNECTED: 1642 call.state = CallState.CALL_DISCONNECTED; 1643 break; 1644 case DISCONNECTING: 1645 call.state = CallState.CALL_DISCONNECTING; 1646 break; 1647 default: 1648 call.state = CallState.CALL_UNKNOWN; 1649 break; 1650 } 1651 call.callEndReason = conn.getDisconnectCause(); 1652 call.isMultiparty = conn.isMultiparty(); 1653 call.preciseDisconnectCause = conn.getPreciseDisconnectCause(); 1654 1655 // Emergency call metrics when call ends 1656 if (conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED 1657 && conn.isEmergencyCall() && conn.getEmergencyNumberInfo() != null) { 1658 /** Only collect this emergency number information per sample percentage */ 1659 if (ThreadLocalRandom.current().nextDouble(0, 100) 1660 < getSamplePercentageForEmergencyCall(countryIso)) { 1661 call.isEmergencyCall = conn.isEmergencyCall(); 1662 call.emergencyNumberInfo = convertEmergencyNumberToEmergencyNumberInfo( 1663 conn.getEmergencyNumberInfo()); 1664 EmergencyNumberTracker emergencyNumberTracker = conn.getEmergencyNumberTracker(); 1665 call.emergencyNumberDatabaseVersion = emergencyNumberTracker != null 1666 ? emergencyNumberTracker.getEmergencyNumberDbVersion() 1667 : TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION; 1668 } 1669 } 1670 } 1671 1672 /** 1673 * Write dial event 1674 * 1675 * @param phoneId Phone id 1676 * @param conn Connection object created to track this call 1677 * @param clirMode CLIR (Calling Line Identification Restriction) mode 1678 * @param uusInfo User-to-User signaling Info 1679 */ writeRilDial(int phoneId, GsmCdmaConnection conn, int clirMode, UUSInfo uusInfo)1680 public void writeRilDial(int phoneId, GsmCdmaConnection conn, int clirMode, UUSInfo uusInfo) { 1681 1682 InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId); 1683 logv("Logging Dial Connection = " + conn); 1684 if (callSession == null) { 1685 Rlog.e(TAG, "writeRilDial: Call session is missing"); 1686 } else { 1687 RilCall[] calls = new RilCall[1]; 1688 calls[0] = new RilCall(); 1689 calls[0].index = -1; 1690 convertConnectionToRilCall(conn, calls[0], ""); 1691 callSession.addEvent(callSession.startElapsedTimeMs, 1692 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST) 1693 .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL) 1694 .setRilCalls(calls)); 1695 logv("Logged Dial event"); 1696 } 1697 } 1698 1699 /** 1700 * Write incoming call event 1701 * 1702 * @param phoneId Phone id 1703 * @param response Unused today 1704 */ writeRilCallRing(int phoneId, char[] response)1705 public void writeRilCallRing(int phoneId, char[] response) { 1706 InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId); 1707 1708 callSession.addEvent(callSession.startElapsedTimeMs, 1709 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_CALL_RING)); 1710 } 1711 1712 /** 1713 * Write call hangup event 1714 * 1715 * @param phoneId Phone id 1716 * @param conn Connection object associated with the call that is being hung-up 1717 * @param callId Call id 1718 */ writeRilHangup(int phoneId, GsmCdmaConnection conn, int callId, String countryIso)1719 public void writeRilHangup(int phoneId, GsmCdmaConnection conn, int callId, 1720 String countryIso) { 1721 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 1722 if (callSession == null) { 1723 Rlog.e(TAG, "writeRilHangup: Call session is missing"); 1724 } else { 1725 RilCall[] calls = new RilCall[1]; 1726 calls[0] = new RilCall(); 1727 calls[0].index = callId; 1728 convertConnectionToRilCall(conn, calls[0], countryIso); 1729 callSession.addEvent( 1730 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST) 1731 .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP) 1732 .setRilCalls(calls)); 1733 logv("Logged Hangup event"); 1734 } 1735 } 1736 1737 /** 1738 * Write call answer event 1739 * 1740 * @param phoneId Phone id 1741 * @param rilSerial RIL request serial number 1742 */ writeRilAnswer(int phoneId, int rilSerial)1743 public void writeRilAnswer(int phoneId, int rilSerial) { 1744 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 1745 if (callSession == null) { 1746 Rlog.e(TAG, "writeRilAnswer: Call session is missing"); 1747 } else { 1748 callSession.addEvent( 1749 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST) 1750 .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_ANSWER) 1751 .setRilRequestId(rilSerial)); 1752 } 1753 } 1754 1755 /** 1756 * Write IMS call SRVCC event 1757 * 1758 * @param phoneId Phone id 1759 * @param rilSrvccState SRVCC state 1760 */ writeRilSrvcc(int phoneId, int rilSrvccState)1761 public void writeRilSrvcc(int phoneId, int rilSrvccState) { 1762 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 1763 if (callSession == null) { 1764 Rlog.e(TAG, "writeRilSrvcc: Call session is missing"); 1765 } else { 1766 callSession.addEvent( 1767 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_CALL_SRVCC) 1768 .setSrvccState(rilSrvccState + 1)); 1769 } 1770 } 1771 1772 /** 1773 * Convert RIL request into proto defined RIL request 1774 * 1775 * @param r RIL request 1776 * @return RIL request defined in call session proto 1777 */ toCallSessionRilRequest(int r)1778 private int toCallSessionRilRequest(int r) { 1779 switch (r) { 1780 case RILConstants.RIL_REQUEST_DIAL: 1781 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL; 1782 1783 case RILConstants.RIL_REQUEST_ANSWER: 1784 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_ANSWER; 1785 1786 case RILConstants.RIL_REQUEST_HANGUP: 1787 case RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: 1788 case RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: 1789 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP; 1790 1791 case RILConstants.RIL_REQUEST_SET_CALL_WAITING: 1792 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_SET_CALL_WAITING; 1793 1794 case RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: 1795 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE; 1796 1797 case RILConstants.RIL_REQUEST_CDMA_FLASH: 1798 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_CDMA_FLASH; 1799 1800 case RILConstants.RIL_REQUEST_CONFERENCE: 1801 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_CONFERENCE; 1802 } 1803 Rlog.e(TAG, "Unknown RIL request: " + r); 1804 return TelephonyCallSession.Event.RilRequest.RIL_REQUEST_UNKNOWN; 1805 } 1806 1807 /** 1808 * Write setup data call response event 1809 * 1810 * @param phoneId Phone id 1811 * @param rilSerial RIL request serial number 1812 * @param rilError RIL error 1813 * @param rilRequest RIL request 1814 * @param result Data call result 1815 */ writeOnSetupDataCallResponse(int phoneId, int rilSerial, int rilError, int rilRequest, DataCallResponse response)1816 private void writeOnSetupDataCallResponse(int phoneId, int rilSerial, int rilError, 1817 int rilRequest, DataCallResponse response) { 1818 1819 RilSetupDataCallResponse setupDataCallResponse = new RilSetupDataCallResponse(); 1820 RilDataCall dataCall = new RilDataCall(); 1821 1822 if (response != null) { 1823 setupDataCallResponse.status = (response.getCause() == 0 1824 ? RilDataCallFailCause.PDP_FAIL_NONE : response.getCause()); 1825 setupDataCallResponse.suggestedRetryTimeMillis = response.getSuggestedRetryTime(); 1826 1827 dataCall.cid = response.getId(); 1828 dataCall.type = response.getProtocolType() + 1; 1829 1830 if (!TextUtils.isEmpty(response.getInterfaceName())) { 1831 dataCall.ifname = response.getInterfaceName(); 1832 } 1833 } 1834 setupDataCallResponse.call = dataCall; 1835 1836 addTelephonyEvent(new TelephonyEventBuilder(phoneId) 1837 .setSetupDataCallResponse(setupDataCallResponse).build()); 1838 } 1839 1840 /** 1841 * Write call related solicited response event 1842 * 1843 * @param phoneId Phone id 1844 * @param rilSerial RIL request serial number 1845 * @param rilError RIL error 1846 * @param rilRequest RIL request 1847 */ writeOnCallSolicitedResponse(int phoneId, int rilSerial, int rilError, int rilRequest)1848 private void writeOnCallSolicitedResponse(int phoneId, int rilSerial, int rilError, 1849 int rilRequest) { 1850 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 1851 if (callSession == null) { 1852 Rlog.e(TAG, "writeOnCallSolicitedResponse: Call session is missing"); 1853 } else { 1854 callSession.addEvent(new CallSessionEventBuilder( 1855 TelephonyCallSession.Event.Type.RIL_RESPONSE) 1856 .setRilRequest(toCallSessionRilRequest(rilRequest)) 1857 .setRilRequestId(rilSerial) 1858 .setRilError(rilError + 1)); 1859 } 1860 } 1861 1862 /** 1863 * Write SMS related solicited response event 1864 * 1865 * @param phoneId Phone id 1866 * @param rilSerial RIL request serial number 1867 * @param rilError RIL error 1868 * @param response SMS response 1869 */ writeOnSmsSolicitedResponse(int phoneId, int rilSerial, int rilError, SmsResponse response)1870 private synchronized void writeOnSmsSolicitedResponse(int phoneId, int rilSerial, int rilError, 1871 SmsResponse response) { 1872 InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId); 1873 if (smsSession == null) { 1874 Rlog.e(TAG, "SMS session is missing"); 1875 } else { 1876 int errorCode = SmsResponse.NO_ERROR_CODE; 1877 long messageId = 0L; 1878 if (response != null) { 1879 errorCode = response.mErrorCode; 1880 messageId = response.mMessageId; 1881 } 1882 1883 smsSession.addEvent(new SmsSessionEventBuilder( 1884 SmsSession.Event.Type.SMS_SEND_RESULT) 1885 .setErrorCode(errorCode) 1886 .setRilErrno(rilError + 1) 1887 .setRilRequestId(rilSerial) 1888 .setMessageId(messageId) 1889 ); 1890 1891 smsSession.decreaseExpectedResponse(); 1892 finishSmsSessionIfNeeded(smsSession); 1893 } 1894 } 1895 1896 /** 1897 * Write SMS related solicited response event 1898 * 1899 * @param phoneId Phone id 1900 * @param errorReason Defined in {@link SmsManager} RESULT_XXX. 1901 * @param messageId Unique id for this message. 1902 */ writeOnImsServiceSmsSolicitedResponse(int phoneId, @ImsSmsImplBase.SendStatusResult int resultCode, int errorReason, long messageId)1903 public synchronized void writeOnImsServiceSmsSolicitedResponse(int phoneId, 1904 @ImsSmsImplBase.SendStatusResult int resultCode, int errorReason, 1905 long messageId) { 1906 1907 InProgressSmsSession smsSession = mInProgressSmsSessions.get(phoneId); 1908 if (smsSession == null) { 1909 Rlog.e(TAG, "SMS session is missing"); 1910 } else { 1911 1912 smsSession.addEvent(new SmsSessionEventBuilder( 1913 SmsSession.Event.Type.SMS_SEND_RESULT) 1914 .setImsServiceErrno(resultCode) 1915 .setErrorCode(errorReason) 1916 .setMessageId(messageId) 1917 ); 1918 1919 smsSession.decreaseExpectedResponse(); 1920 finishSmsSessionIfNeeded(smsSession); 1921 } 1922 } 1923 1924 /** 1925 * Write deactivate data call response event 1926 * 1927 * @param phoneId Phone id 1928 * @param rilError RIL error 1929 */ writeOnDeactivateDataCallResponse(int phoneId, int rilError)1930 private void writeOnDeactivateDataCallResponse(int phoneId, int rilError) { 1931 addTelephonyEvent(new TelephonyEventBuilder(phoneId) 1932 .setDeactivateDataCallResponse(rilError + 1).build()); 1933 } 1934 1935 /** 1936 * Write RIL solicited response event 1937 * 1938 * @param phoneId Phone id 1939 * @param rilSerial RIL request serial number 1940 * @param rilError RIL error 1941 * @param rilRequest RIL request 1942 * @param ret The returned RIL response 1943 */ writeOnRilSolicitedResponse(int phoneId, int rilSerial, int rilError, int rilRequest, Object ret)1944 public void writeOnRilSolicitedResponse(int phoneId, int rilSerial, int rilError, 1945 int rilRequest, Object ret) { 1946 switch (rilRequest) { 1947 case RIL_REQUEST_SETUP_DATA_CALL: 1948 DataCallResponse response = (DataCallResponse) ret; 1949 writeOnSetupDataCallResponse(phoneId, rilSerial, rilError, rilRequest, response); 1950 break; 1951 case RIL_REQUEST_DEACTIVATE_DATA_CALL: 1952 writeOnDeactivateDataCallResponse(phoneId, rilError); 1953 break; 1954 case RIL_REQUEST_HANGUP: 1955 case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: 1956 case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: 1957 case RIL_REQUEST_DIAL: 1958 case RIL_REQUEST_ANSWER: 1959 writeOnCallSolicitedResponse(phoneId, rilSerial, rilError, rilRequest); 1960 break; 1961 case RIL_REQUEST_SEND_SMS: 1962 case RIL_REQUEST_SEND_SMS_EXPECT_MORE: 1963 case RIL_REQUEST_CDMA_SEND_SMS: 1964 case RIL_REQUEST_IMS_SEND_SMS: 1965 SmsResponse smsResponse = (SmsResponse) ret; 1966 writeOnSmsSolicitedResponse(phoneId, rilSerial, rilError, smsResponse); 1967 break; 1968 } 1969 } 1970 1971 /** 1972 * Write network validation event. 1973 * @param networkValidationState the network validation state. 1974 */ writeNetworkValidate(int networkValidationState)1975 public void writeNetworkValidate(int networkValidationState) { 1976 addTelephonyEvent( 1977 new TelephonyEventBuilder().setNetworkValidate(networkValidationState).build()); 1978 } 1979 1980 /** 1981 * Write data switch event. 1982 * @param subId data switch to the subscription with this id. 1983 * @param dataSwitch the reason and state of data switch. 1984 */ writeDataSwitch(int subId, DataSwitch dataSwitch)1985 public void writeDataSwitch(int subId, DataSwitch dataSwitch) { 1986 int phoneId = SubscriptionManager.getPhoneId(subId); 1987 addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDataSwitch(dataSwitch).build()); 1988 } 1989 1990 /** 1991 * Write on demand data switch event. 1992 * @param onDemandDataSwitch the apn and state of on demand data switch. 1993 */ writeOnDemandDataSwitch(OnDemandDataSwitch onDemandDataSwitch)1994 public void writeOnDemandDataSwitch(OnDemandDataSwitch onDemandDataSwitch) { 1995 addTelephonyEvent( 1996 new TelephonyEventBuilder().setOnDemandDataSwitch(onDemandDataSwitch).build()); 1997 } 1998 1999 /** 2000 * Write phone state changed event 2001 * 2002 * @param phoneId Phone id 2003 * @param phoneState Phone state. See PhoneConstants.State for the details. 2004 */ writePhoneState(int phoneId, PhoneConstants.State phoneState)2005 public void writePhoneState(int phoneId, PhoneConstants.State phoneState) { 2006 int state; 2007 switch (phoneState) { 2008 case IDLE: 2009 state = TelephonyCallSession.Event.PhoneState.STATE_IDLE; 2010 break; 2011 case RINGING: 2012 state = TelephonyCallSession.Event.PhoneState.STATE_RINGING; 2013 break; 2014 case OFFHOOK: 2015 state = TelephonyCallSession.Event.PhoneState.STATE_OFFHOOK; 2016 break; 2017 default: 2018 state = TelephonyCallSession.Event.PhoneState.STATE_UNKNOWN; 2019 break; 2020 } 2021 2022 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 2023 if (callSession == null) { 2024 Rlog.e(TAG, "writePhoneState: Call session is missing"); 2025 } else { 2026 // For CS Calls Finish the Call Session after Receiving the Last Call Fail Cause 2027 // For IMS calls we receive the Disconnect Cause along with Call End event. 2028 // So we can finish the call session here. 2029 callSession.setLastKnownPhoneState(state); 2030 if ((state == TelephonyCallSession.Event.PhoneState.STATE_IDLE) 2031 && (!callSession.containsCsCalls())) { 2032 finishCallSession(callSession); 2033 } 2034 callSession.addEvent(new CallSessionEventBuilder( 2035 TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED) 2036 .setPhoneState(state)); 2037 } 2038 } 2039 2040 /** 2041 * Extracts the call ID from an ImsSession. 2042 * 2043 * @param session The session. 2044 * @return The call ID for the session, or -1 if none was found. 2045 */ getCallId(ImsCallSession session)2046 private int getCallId(ImsCallSession session) { 2047 if (session == null) { 2048 return -1; 2049 } 2050 2051 try { 2052 return Integer.parseInt(session.getCallId()); 2053 } catch (NumberFormatException nfe) { 2054 return -1; 2055 } 2056 } 2057 2058 /** 2059 * Write IMS call state changed event 2060 * 2061 * @param phoneId Phone id 2062 * @param session IMS call session 2063 * @param callState IMS call state 2064 */ writeImsCallState(int phoneId, ImsCallSession session, ImsPhoneCall.State callState)2065 public void writeImsCallState(int phoneId, ImsCallSession session, 2066 ImsPhoneCall.State callState) { 2067 int state; 2068 switch (callState) { 2069 case IDLE: 2070 state = TelephonyCallSession.Event.CallState.CALL_IDLE; break; 2071 case ACTIVE: 2072 state = TelephonyCallSession.Event.CallState.CALL_ACTIVE; break; 2073 case HOLDING: 2074 state = TelephonyCallSession.Event.CallState.CALL_HOLDING; break; 2075 case DIALING: 2076 state = TelephonyCallSession.Event.CallState.CALL_DIALING; break; 2077 case ALERTING: 2078 state = TelephonyCallSession.Event.CallState.CALL_ALERTING; break; 2079 case INCOMING: 2080 state = TelephonyCallSession.Event.CallState.CALL_INCOMING; break; 2081 case WAITING: 2082 state = TelephonyCallSession.Event.CallState.CALL_WAITING; break; 2083 case DISCONNECTED: 2084 state = TelephonyCallSession.Event.CallState.CALL_DISCONNECTED; break; 2085 case DISCONNECTING: 2086 state = TelephonyCallSession.Event.CallState.CALL_DISCONNECTING; break; 2087 default: 2088 state = TelephonyCallSession.Event.CallState.CALL_UNKNOWN; break; 2089 } 2090 2091 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 2092 if (callSession == null) { 2093 Rlog.e(TAG, "Call session is missing"); 2094 } else { 2095 callSession.addEvent(new CallSessionEventBuilder( 2096 TelephonyCallSession.Event.Type.IMS_CALL_STATE_CHANGED) 2097 .setCallIndex(getCallId(session)) 2098 .setCallState(state)); 2099 } 2100 } 2101 2102 /** 2103 * Write IMS call start event 2104 * 2105 * @param phoneId Phone id 2106 * @param session IMS call session 2107 */ writeOnImsCallStart(int phoneId, ImsCallSession session)2108 public void writeOnImsCallStart(int phoneId, ImsCallSession session) { 2109 InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId); 2110 2111 callSession.addEvent( 2112 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_COMMAND) 2113 .setCallIndex(getCallId(session)) 2114 .setImsCommand(TelephonyCallSession.Event.ImsCommand.IMS_CMD_START)); 2115 } 2116 2117 /** 2118 * Write IMS incoming call event 2119 * 2120 * @param phoneId Phone id 2121 * @param session IMS call session 2122 */ writeOnImsCallReceive(int phoneId, ImsCallSession session)2123 public void writeOnImsCallReceive(int phoneId, ImsCallSession session) { 2124 InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId); 2125 2126 callSession.addEvent( 2127 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_CALL_RECEIVE) 2128 .setCallIndex(getCallId(session))); 2129 } 2130 2131 /** 2132 * Write IMS command event 2133 * 2134 * @param phoneId Phone id 2135 * @param session IMS call session 2136 * @param command IMS command 2137 */ writeOnImsCommand(int phoneId, ImsCallSession session, int command)2138 public void writeOnImsCommand(int phoneId, ImsCallSession session, int command) { 2139 2140 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 2141 if (callSession == null) { 2142 Rlog.e(TAG, "Call session is missing"); 2143 } else { 2144 callSession.addEvent( 2145 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_COMMAND) 2146 .setCallIndex(getCallId(session)) 2147 .setImsCommand(command)); 2148 } 2149 } 2150 2151 /** 2152 * Convert IMS reason info into proto 2153 * 2154 * @param reasonInfo IMS reason info 2155 * @return Converted proto 2156 */ toImsReasonInfoProto(ImsReasonInfo reasonInfo)2157 private TelephonyProto.ImsReasonInfo toImsReasonInfoProto(ImsReasonInfo reasonInfo) { 2158 TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo(); 2159 if (reasonInfo != null) { 2160 ri.reasonCode = reasonInfo.getCode(); 2161 ri.extraCode = reasonInfo.getExtraCode(); 2162 String extraMessage = reasonInfo.getExtraMessage(); 2163 if (extraMessage != null) { 2164 ri.extraMessage = extraMessage; 2165 } 2166 } 2167 return ri; 2168 } 2169 2170 /** 2171 * Convert CallQuality to proto. 2172 * 2173 * @param callQuality call quality to convert 2174 * @return Coverted proto 2175 */ toCallQualityProto( CallQuality callQuality)2176 public static TelephonyCallSession.Event.CallQuality toCallQualityProto( 2177 CallQuality callQuality) { 2178 TelephonyCallSession.Event.CallQuality cq = new TelephonyCallSession.Event.CallQuality(); 2179 if (callQuality != null) { 2180 cq.downlinkLevel = callQualityLevelToProtoEnum(callQuality 2181 .getDownlinkCallQualityLevel()); 2182 cq.uplinkLevel = callQualityLevelToProtoEnum(callQuality.getUplinkCallQualityLevel()); 2183 // callDuration is reported in millis, so convert to seconds 2184 cq.durationInSeconds = callQuality.getCallDuration() / 1000; 2185 cq.rtpPacketsTransmitted = callQuality.getNumRtpPacketsTransmitted(); 2186 cq.rtpPacketsReceived = callQuality.getNumRtpPacketsReceived(); 2187 cq.rtpPacketsTransmittedLost = callQuality.getNumRtpPacketsTransmittedLost(); 2188 cq.rtpPacketsNotReceived = callQuality.getNumRtpPacketsNotReceived(); 2189 cq.averageRelativeJitterMillis = callQuality.getAverageRelativeJitter(); 2190 cq.maxRelativeJitterMillis = callQuality.getMaxRelativeJitter(); 2191 cq.codecType = convertImsCodec(callQuality.getCodecType()); 2192 cq.rtpInactivityDetected = callQuality.isRtpInactivityDetected(); 2193 cq.rxSilenceDetected = callQuality.isIncomingSilenceDetectedAtCallSetup(); 2194 cq.txSilenceDetected = callQuality.isOutgoingSilenceDetectedAtCallSetup(); 2195 } 2196 return cq; 2197 } 2198 2199 /** 2200 * Convert Call quality level into proto defined value. 2201 */ callQualityLevelToProtoEnum(int level)2202 private static int callQualityLevelToProtoEnum(int level) { 2203 if (level == CallQuality.CALL_QUALITY_EXCELLENT) { 2204 return TelephonyCallSession.Event.CallQuality.CallQualityLevel.EXCELLENT; 2205 } else if (level == CallQuality.CALL_QUALITY_GOOD) { 2206 return TelephonyCallSession.Event.CallQuality.CallQualityLevel.GOOD; 2207 } else if (level == CallQuality.CALL_QUALITY_FAIR) { 2208 return TelephonyCallSession.Event.CallQuality.CallQualityLevel.FAIR; 2209 } else if (level == CallQuality.CALL_QUALITY_POOR) { 2210 return TelephonyCallSession.Event.CallQuality.CallQualityLevel.POOR; 2211 } else if (level == CallQuality.CALL_QUALITY_BAD) { 2212 return TelephonyCallSession.Event.CallQuality.CallQualityLevel.BAD; 2213 } else if (level == CallQuality.CALL_QUALITY_NOT_AVAILABLE) { 2214 return TelephonyCallSession.Event.CallQuality.CallQualityLevel.NOT_AVAILABLE; 2215 } else { 2216 return TelephonyCallSession.Event.CallQuality.CallQualityLevel.UNDEFINED; 2217 } 2218 } 2219 2220 /** 2221 * Write IMS call end event 2222 * 2223 * @param phoneId Phone id 2224 * @param session IMS call session 2225 * @param reasonInfo Call end reason 2226 * @param cqm Call Quality Metrics 2227 * @param emergencyNumber Emergency Number Info 2228 * @param countryIso Network country iso 2229 * @param emergencyNumberDatabaseVersion Emergency Number Database Version 2230 */ writeOnImsCallTerminated(int phoneId, ImsCallSession session, ImsReasonInfo reasonInfo, CallQualityMetrics cqm, EmergencyNumber emergencyNumber, String countryIso, int emergencyNumberDatabaseVersion)2231 public void writeOnImsCallTerminated(int phoneId, ImsCallSession session, 2232 ImsReasonInfo reasonInfo, CallQualityMetrics cqm, 2233 EmergencyNumber emergencyNumber, String countryIso, 2234 int emergencyNumberDatabaseVersion) { 2235 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 2236 if (callSession == null) { 2237 Rlog.e(TAG, "Call session is missing"); 2238 } else { 2239 CallSessionEventBuilder callSessionEvent = new CallSessionEventBuilder( 2240 TelephonyCallSession.Event.Type.IMS_CALL_TERMINATED); 2241 callSessionEvent.setCallIndex(getCallId(session)); 2242 callSessionEvent.setImsReasonInfo(toImsReasonInfoProto(reasonInfo)); 2243 2244 if (cqm != null) { 2245 callSessionEvent.setCallQualitySummaryDl(cqm.getCallQualitySummaryDl()) 2246 .setCallQualitySummaryUl(cqm.getCallQualitySummaryUl()); 2247 } 2248 2249 if (emergencyNumber != null) { 2250 /** Only collect this emergency number information per sample percentage */ 2251 if (ThreadLocalRandom.current().nextDouble(0, 100) 2252 < getSamplePercentageForEmergencyCall(countryIso)) { 2253 callSessionEvent.setIsImsEmergencyCall(true); 2254 callSessionEvent.setImsEmergencyNumberInfo( 2255 convertEmergencyNumberToEmergencyNumberInfo(emergencyNumber)); 2256 callSessionEvent.setEmergencyNumberDatabaseVersion( 2257 emergencyNumberDatabaseVersion); 2258 } 2259 } 2260 callSession.addEvent(callSessionEvent); 2261 } 2262 } 2263 2264 /** 2265 * Write IMS call hangover event 2266 * 2267 * @param phoneId Phone id 2268 * @param eventType hangover type 2269 * @param session IMS call session 2270 * @param srcAccessTech Hangover starting RAT 2271 * @param targetAccessTech Hangover destination RAT 2272 * @param reasonInfo Hangover reason 2273 */ writeOnImsCallHandoverEvent(int phoneId, int eventType, ImsCallSession session, int srcAccessTech, int targetAccessTech, ImsReasonInfo reasonInfo)2274 public void writeOnImsCallHandoverEvent(int phoneId, int eventType, ImsCallSession session, 2275 int srcAccessTech, int targetAccessTech, 2276 ImsReasonInfo reasonInfo) { 2277 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 2278 if (callSession == null) { 2279 Rlog.e(TAG, "Call session is missing"); 2280 } else { 2281 callSession.addEvent( 2282 new CallSessionEventBuilder(eventType) 2283 .setCallIndex(getCallId(session)) 2284 .setSrcAccessTech(srcAccessTech) 2285 .setTargetAccessTech(targetAccessTech) 2286 .setImsReasonInfo(toImsReasonInfoProto(reasonInfo))); 2287 } 2288 } 2289 2290 /** 2291 * Write Send SMS event 2292 * 2293 * @param phoneId Phone id 2294 * @param rilSerial RIL request serial number 2295 * @param tech SMS RAT 2296 * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or 2297 * {@link SmsMessage#FORMAT_3GPP2}. 2298 * @param messageId Unique id for this message. 2299 */ writeRilSendSms(int phoneId, int rilSerial, int tech, int format, long messageId)2300 public synchronized void writeRilSendSms(int phoneId, int rilSerial, int tech, int format, 2301 long messageId) { 2302 InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId); 2303 2304 smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND) 2305 .setTech(tech) 2306 .setRilRequestId(rilSerial) 2307 .setFormat(format) 2308 .setMessageId(messageId) 2309 ); 2310 2311 smsSession.increaseExpectedResponse(); 2312 } 2313 2314 /** 2315 * Write Send SMS event using ImsService. Expecting response from 2316 * {@link #writeOnSmsSolicitedResponse}. 2317 * 2318 * @param phoneId Phone id 2319 * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or 2320 * {@link SmsMessage#FORMAT_3GPP2}. 2321 * @param resultCode The result of sending the new SMS to the vendor layer to be sent to the 2322 * carrier network. 2323 * @param messageId Unique id for this message. 2324 */ writeImsServiceSendSms(int phoneId, String format, @ImsSmsImplBase.SendStatusResult int resultCode, long messageId)2325 public synchronized void writeImsServiceSendSms(int phoneId, String format, 2326 @ImsSmsImplBase.SendStatusResult int resultCode, long messageId) { 2327 InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId); 2328 smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_SEND) 2329 .setTech(SmsSession.Event.Tech.SMS_IMS) 2330 .setImsServiceErrno(resultCode) 2331 .setFormat(convertSmsFormat(format)) 2332 .setMessageId(messageId) 2333 ); 2334 2335 smsSession.increaseExpectedResponse(); 2336 } 2337 2338 /** 2339 * Write incoming Broadcast SMS event 2340 * 2341 * @param phoneId Phone id 2342 * @param format CB msg format 2343 * @param priority CB msg priority 2344 * @param isCMAS true if msg is CMAS 2345 * @param isETWS true if msg is ETWS 2346 * @param serviceCategory Service category of CB msg 2347 * @param serialNumber Serial number of the message 2348 * @param deliveredTimestamp Message's delivered timestamp 2349 */ writeNewCBSms(int phoneId, int format, int priority, boolean isCMAS, boolean isETWS, int serviceCategory, int serialNumber, long deliveredTimestamp)2350 public synchronized void writeNewCBSms(int phoneId, int format, int priority, boolean isCMAS, 2351 boolean isETWS, int serviceCategory, int serialNumber, 2352 long deliveredTimestamp) { 2353 InProgressSmsSession smsSession = startNewSmsSessionIfNeeded(phoneId); 2354 2355 int type; 2356 if (isCMAS) { 2357 type = SmsSession.Event.CBMessageType.CMAS; 2358 } else if (isETWS) { 2359 type = SmsSession.Event.CBMessageType.ETWS; 2360 } else { 2361 type = SmsSession.Event.CBMessageType.OTHER; 2362 } 2363 2364 SmsSession.Event.CBMessage cbm = new SmsSession.Event.CBMessage(); 2365 cbm.msgFormat = format; 2366 cbm.msgPriority = priority + 1; 2367 cbm.msgType = type; 2368 cbm.serviceCategory = serviceCategory; 2369 cbm.serialNumber = serialNumber; 2370 cbm.deliveredTimestampMillis = deliveredTimestamp; 2371 2372 smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.CB_SMS_RECEIVED) 2373 .setCellBroadcastMessage(cbm) 2374 ); 2375 2376 finishSmsSessionIfNeeded(smsSession); 2377 } 2378 2379 /** 2380 * Write an incoming multi-part SMS that was discarded because some parts were missing 2381 * 2382 * @param phoneId Phone id 2383 * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or 2384 * {@link SmsMessage#FORMAT_3GPP2}. 2385 * @param receivedCount Number of received parts. 2386 * @param totalCount Total number of parts of the SMS. 2387 */ writeDroppedIncomingMultipartSms(int phoneId, String format, int receivedCount, int totalCount)2388 public void writeDroppedIncomingMultipartSms(int phoneId, String format, 2389 int receivedCount, int totalCount) { 2390 logv("Logged dropped multipart SMS: received " + receivedCount 2391 + " out of " + totalCount); 2392 2393 SmsSession.Event.IncompleteSms details = new SmsSession.Event.IncompleteSms(); 2394 details.receivedParts = receivedCount; 2395 details.totalParts = totalCount; 2396 2397 InProgressSmsSession smsSession = startNewSmsSession(phoneId); 2398 smsSession.addEvent( 2399 new SmsSessionEventBuilder(SmsSession.Event.Type.INCOMPLETE_SMS_RECEIVED) 2400 .setFormat(convertSmsFormat(format)) 2401 .setIncompleteSms(details)); 2402 2403 finishSmsSession(smsSession); 2404 } 2405 2406 /** 2407 * Write a generic SMS of any type 2408 * 2409 * @param phoneId Phone id 2410 * @param type Type of the SMS. 2411 * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or 2412 * {@link SmsMessage#FORMAT_3GPP2}. 2413 * @param success Indicates if the SMS-PP was successfully delivered to the USIM. 2414 */ writeIncomingSmsWithType(int phoneId, int type, String format, boolean success)2415 private void writeIncomingSmsWithType(int phoneId, int type, String format, boolean success) { 2416 InProgressSmsSession smsSession = startNewSmsSession(phoneId); 2417 smsSession.addEvent(new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED) 2418 .setFormat(convertSmsFormat(format)) 2419 .setSmsType(type) 2420 .setErrorCode(success ? SmsManager.RESULT_ERROR_NONE : 2421 SmsManager.RESULT_ERROR_GENERIC_FAILURE)); 2422 finishSmsSession(smsSession); 2423 } 2424 2425 /** 2426 * Write an incoming SMS-PP for the USIM 2427 * 2428 * @param phoneId Phone id 2429 * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or 2430 * {@link SmsMessage#FORMAT_3GPP2}. 2431 * @param success Indicates if the SMS-PP was successfully delivered to the USIM. 2432 */ writeIncomingSMSPP(int phoneId, String format, boolean success)2433 public void writeIncomingSMSPP(int phoneId, String format, boolean success) { 2434 logv("Logged SMS-PP session. Result = " + success); 2435 writeIncomingSmsWithType(phoneId, 2436 SmsSession.Event.SmsType.SMS_TYPE_SMS_PP, format, success); 2437 } 2438 2439 /** 2440 * Write an incoming SMS to update voicemail indicator 2441 * 2442 * @param phoneId Phone id 2443 * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or 2444 * {@link SmsMessage#FORMAT_3GPP2}. 2445 */ writeIncomingVoiceMailSms(int phoneId, String format)2446 public void writeIncomingVoiceMailSms(int phoneId, String format) { 2447 logv("Logged VoiceMail message."); 2448 writeIncomingSmsWithType(phoneId, 2449 SmsSession.Event.SmsType.SMS_TYPE_VOICEMAIL_INDICATION, format, true); 2450 } 2451 2452 /** 2453 * Write an incoming SMS of type 0 2454 * 2455 * @param phoneId Phone id 2456 * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or 2457 * {@link SmsMessage#FORMAT_3GPP2}. 2458 */ writeIncomingSmsTypeZero(int phoneId, String format)2459 public void writeIncomingSmsTypeZero(int phoneId, String format) { 2460 logv("Logged Type-0 SMS message."); 2461 writeIncomingSmsWithType(phoneId, 2462 SmsSession.Event.SmsType.SMS_TYPE_ZERO, format, true); 2463 } 2464 2465 /** 2466 * Write a successful incoming SMS session 2467 * 2468 * @param phoneId Phone id 2469 * @param type Type of the SMS. 2470 * @param smsSource the source of the SMS message 2471 * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or 2472 * {@link SmsMessage#FORMAT_3GPP2}. 2473 * @param timestamps array with timestamps of each incoming SMS part. It contains a single 2474 * @param blocked indicates if the message was blocked or not. 2475 * @param success Indicates if the SMS-PP was successfully delivered to the USIM. 2476 * @param messageId Unique id for this message. 2477 */ writeIncomingSmsSessionWithType(int phoneId, int type, @InboundSmsHandler.SmsSource int smsSource, String format, long[] timestamps, boolean blocked, boolean success, long messageId)2478 private void writeIncomingSmsSessionWithType(int phoneId, int type, 2479 @InboundSmsHandler.SmsSource int smsSource, String format, long[] timestamps, 2480 boolean blocked, boolean success, long messageId) { 2481 logv("Logged SMS session consisting of " + timestamps.length 2482 + " parts, source = " + smsSource 2483 + " blocked = " + blocked 2484 + " type = " + type 2485 + " " + SmsController.formatCrossStackMessageId(messageId)); 2486 2487 int smsFormat = convertSmsFormat(format); 2488 int smsError = 2489 success ? SmsManager.RESULT_ERROR_NONE : SmsManager.RESULT_ERROR_GENERIC_FAILURE; 2490 int smsTech = getSmsTech(smsSource, smsFormat == SmsSession.Event.Format.SMS_FORMAT_3GPP2); 2491 2492 InProgressSmsSession smsSession = startNewSmsSession(phoneId); 2493 2494 long startElapsedTimeMillis = SystemClock.elapsedRealtime(); 2495 for (int i = 0; i < timestamps.length; i++) { 2496 SmsSessionEventBuilder eventBuilder = 2497 new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED) 2498 .setFormat(smsFormat) 2499 .setTech(smsTech) 2500 .setErrorCode(smsError) 2501 .setSmsType(type) 2502 .setBlocked(blocked) 2503 .setMessageId(messageId); 2504 long interval = (i > 0) ? timestamps[i] - timestamps[i - 1] : 0; 2505 smsSession.addEvent(startElapsedTimeMillis + interval, eventBuilder); 2506 } 2507 finishSmsSession(smsSession); 2508 } 2509 2510 /** 2511 * Write an incoming WAP-PUSH message. 2512 * 2513 * @param phoneId Phone id 2514 * @param smsSource the source of the SMS message 2515 * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or 2516 * {@link SmsMessage#FORMAT_3GPP2}. 2517 * @param timestamps array with timestamps of each incoming SMS part. It contains a single 2518 * @param success Indicates if the SMS-PP was successfully delivered to the USIM. 2519 * @param messageId Unique id for this message. 2520 */ writeIncomingWapPush(int phoneId, @InboundSmsHandler.SmsSource int smsSource, String format, long[] timestamps, boolean success, long messageId)2521 public void writeIncomingWapPush(int phoneId, @InboundSmsHandler.SmsSource int smsSource, 2522 String format, long[] timestamps, boolean success, long messageId) { 2523 writeIncomingSmsSessionWithType(phoneId, SmsSession.Event.SmsType.SMS_TYPE_WAP_PUSH, 2524 smsSource, format, timestamps, false, success, messageId); 2525 } 2526 2527 /** 2528 * Write a successful incoming SMS session 2529 * 2530 * @param phoneId Phone id 2531 * @param smsSource the source of the SMS message 2532 * @param format SMS format. Either {@link SmsMessage#FORMAT_3GPP} or 2533 * {@link SmsMessage#FORMAT_3GPP2}. 2534 * @param timestamps array with timestamps of each incoming SMS part. It contains a single 2535 * @param blocked indicates if the message was blocked or not. 2536 * @param messageId Unique id for this message. 2537 */ writeIncomingSmsSession(int phoneId, @InboundSmsHandler.SmsSource int smsSource, String format, long[] timestamps, boolean blocked, long messageId)2538 public void writeIncomingSmsSession(int phoneId, @InboundSmsHandler.SmsSource int smsSource, 2539 String format, long[] timestamps, boolean blocked, long messageId) { 2540 writeIncomingSmsSessionWithType(phoneId, SmsSession.Event.SmsType.SMS_TYPE_NORMAL, 2541 smsSource, format, timestamps, blocked, true, messageId); 2542 } 2543 2544 /** 2545 * Write an error incoming SMS 2546 * 2547 * @param phoneId Phone id 2548 * @param is3gpp2 true for 3GPP2 format, false for 3GPP format. 2549 * @param smsSource the source of the SMS message 2550 * @param result Indicates the reason of the failure. 2551 */ writeIncomingSmsError(int phoneId, boolean is3gpp2, @InboundSmsHandler.SmsSource int smsSource, int result)2552 public void writeIncomingSmsError(int phoneId, boolean is3gpp2, 2553 @InboundSmsHandler.SmsSource int smsSource, int result) { 2554 logv("Incoming SMS error = " + result); 2555 2556 int smsError = SmsManager.RESULT_ERROR_GENERIC_FAILURE; 2557 switch (result) { 2558 case Intents.RESULT_SMS_HANDLED: 2559 // This should not happen. 2560 return; 2561 case Intents.RESULT_SMS_OUT_OF_MEMORY: 2562 smsError = SmsManager.RESULT_NO_MEMORY; 2563 break; 2564 case Intents.RESULT_SMS_UNSUPPORTED: 2565 smsError = SmsManager.RESULT_REQUEST_NOT_SUPPORTED; 2566 break; 2567 case Intents.RESULT_SMS_GENERIC_ERROR: 2568 default: 2569 smsError = SmsManager.RESULT_ERROR_GENERIC_FAILURE; 2570 break; 2571 } 2572 2573 InProgressSmsSession smsSession = startNewSmsSession(phoneId); 2574 2575 SmsSessionEventBuilder eventBuilder = 2576 new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED) 2577 .setFormat(is3gpp2 2578 ? SmsSession.Event.Format.SMS_FORMAT_3GPP2 2579 : SmsSession.Event.Format.SMS_FORMAT_3GPP) 2580 .setTech(getSmsTech(smsSource, is3gpp2)) 2581 .setErrorCode(smsError); 2582 smsSession.addEvent(eventBuilder); 2583 finishSmsSession(smsSession); 2584 } 2585 2586 /** 2587 * Write NITZ event 2588 * 2589 * @param phoneId Phone id 2590 * @param timestamp NITZ time in milliseconds 2591 */ writeNITZEvent(int phoneId, long timestamp)2592 public void writeNITZEvent(int phoneId, long timestamp) { 2593 TelephonyEvent event = new TelephonyEventBuilder(phoneId).setNITZ(timestamp).build(); 2594 addTelephonyEvent(event); 2595 2596 annotateInProgressCallSession(event.timestampMillis, phoneId, 2597 new CallSessionEventBuilder( 2598 TelephonyCallSession.Event.Type.NITZ_TIME) 2599 .setNITZ(timestamp)); 2600 } 2601 2602 /** 2603 * Write Modem Restart event 2604 * 2605 * @param phoneId Phone id 2606 * @param reason Reason for the modem reset. 2607 */ writeModemRestartEvent(int phoneId, String reason)2608 public void writeModemRestartEvent(int phoneId, String reason) { 2609 final ModemRestart modemRestart = new ModemRestart(); 2610 String basebandVersion = Build.getRadioVersion(); 2611 if (basebandVersion != null) modemRestart.basebandVersion = basebandVersion; 2612 if (reason != null) modemRestart.reason = reason; 2613 TelephonyEvent event = new TelephonyEventBuilder(phoneId).setModemRestart( 2614 modemRestart).build(); 2615 addTelephonyEvent(event); 2616 } 2617 2618 /** 2619 * Write carrier identification matching event 2620 * 2621 * @param phoneId Phone id 2622 * @param version Carrier table version 2623 * @param cid Unique Carrier Id 2624 * @param unknownMcmnc MCC and MNC that map to this carrier 2625 * @param unknownGid1 Group id level 1 2626 * @param simInfo Subscription info 2627 */ writeCarrierIdMatchingEvent(int phoneId, int version, int cid, String unknownMcmnc, String unknownGid1, CarrierResolver.CarrierMatchingRule simInfo)2628 public void writeCarrierIdMatchingEvent(int phoneId, int version, int cid, 2629 String unknownMcmnc, String unknownGid1, 2630 CarrierResolver.CarrierMatchingRule simInfo) { 2631 final CarrierIdMatching carrierIdMatching = new CarrierIdMatching(); 2632 final CarrierIdMatchingResult carrierIdMatchingResult = new CarrierIdMatchingResult(); 2633 2634 // fill in information for unknown mccmnc and gid1 for unidentified carriers. 2635 if (cid != TelephonyManager.UNKNOWN_CARRIER_ID) { 2636 // Successful matching event if result only has carrierId 2637 carrierIdMatchingResult.carrierId = cid; 2638 // Unknown Gid1 event if result only has carrierId, gid1 and mccmnc 2639 if (unknownGid1 != null) { 2640 carrierIdMatchingResult.unknownMccmnc = unknownMcmnc; 2641 carrierIdMatchingResult.unknownGid1 = unknownGid1; 2642 } 2643 } else { 2644 // Unknown mccmnc event if result only has mccmnc 2645 if (unknownMcmnc != null) { 2646 carrierIdMatchingResult.unknownMccmnc = unknownMcmnc; 2647 } 2648 } 2649 2650 // fill in complete matching information from the SIM. 2651 carrierIdMatchingResult.mccmnc = TelephonyUtils.emptyIfNull(simInfo.mccMnc); 2652 carrierIdMatchingResult.spn = TelephonyUtils.emptyIfNull(simInfo.spn); 2653 carrierIdMatchingResult.pnn = TelephonyUtils.emptyIfNull(simInfo.plmn); 2654 carrierIdMatchingResult.gid1 = TelephonyUtils.emptyIfNull(simInfo.gid1); 2655 carrierIdMatchingResult.gid2 = TelephonyUtils.emptyIfNull(simInfo.gid2); 2656 carrierIdMatchingResult.imsiPrefix = TelephonyUtils.emptyIfNull(simInfo.imsiPrefixPattern); 2657 carrierIdMatchingResult.iccidPrefix = TelephonyUtils.emptyIfNull(simInfo.iccidPrefix); 2658 carrierIdMatchingResult.preferApn = TelephonyUtils.emptyIfNull(simInfo.apn); 2659 if (simInfo.privilegeAccessRule != null) { 2660 carrierIdMatchingResult.privilegeAccessRule = 2661 simInfo.privilegeAccessRule.stream().toArray(String[]::new); 2662 } 2663 2664 carrierIdMatching.cidTableVersion = version; 2665 carrierIdMatching.result = carrierIdMatchingResult; 2666 2667 TelephonyEvent event = new TelephonyEventBuilder(phoneId).setCarrierIdMatching( 2668 carrierIdMatching).build(); 2669 mLastCarrierId.put(phoneId, carrierIdMatching); 2670 addTelephonyEvent(event); 2671 } 2672 2673 /** 2674 * Write emergency number update event 2675 * 2676 * @param emergencyNumber Updated emergency number 2677 */ writeEmergencyNumberUpdateEvent(int phoneId, EmergencyNumber emergencyNumber, int emergencyNumberDatabaseVersion)2678 public void writeEmergencyNumberUpdateEvent(int phoneId, EmergencyNumber emergencyNumber, 2679 int emergencyNumberDatabaseVersion) { 2680 if (emergencyNumber == null) { 2681 return; 2682 } 2683 final EmergencyNumberInfo emergencyNumberInfo = 2684 convertEmergencyNumberToEmergencyNumberInfo(emergencyNumber); 2685 2686 TelephonyEvent event = new TelephonyEventBuilder(phoneId).setUpdatedEmergencyNumber( 2687 emergencyNumberInfo, emergencyNumberDatabaseVersion).build(); 2688 addTelephonyEvent(event); 2689 } 2690 2691 /** 2692 * Write network capabilities changed event 2693 * 2694 * @param phoneId Phone id 2695 * @param networkCapabilities Network capabilities 2696 */ writeNetworkCapabilitiesChangedEvent(int phoneId, NetworkCapabilities networkCapabilities)2697 public void writeNetworkCapabilitiesChangedEvent(int phoneId, 2698 NetworkCapabilities networkCapabilities) { 2699 final NetworkCapabilitiesInfo caps = new NetworkCapabilitiesInfo(); 2700 caps.isNetworkUnmetered = networkCapabilities.hasCapability( 2701 NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED); 2702 2703 TelephonyEvent event = new TelephonyEventBuilder(phoneId) 2704 .setNetworkCapabilities(caps).build(); 2705 mLastNetworkCapabilitiesInfos.put(phoneId, caps); 2706 addTelephonyEvent(event); 2707 } 2708 2709 /** Write radio state changed event */ writeRadioState(int phoneId, @RadioPowerState int state)2710 public void writeRadioState(int phoneId, @RadioPowerState int state) { 2711 int radioState = convertRadioState(state); 2712 TelephonyEvent event = new TelephonyEventBuilder(phoneId).setRadioState(radioState).build(); 2713 mLastRadioState.put(phoneId, radioState); 2714 addTelephonyEvent(event); 2715 } 2716 convertRadioState(@adioPowerState int state)2717 private static int convertRadioState(@RadioPowerState int state) { 2718 switch (state) { 2719 case TelephonyManager.RADIO_POWER_OFF: 2720 return RadioState.RADIO_STATE_OFF; 2721 case TelephonyManager.RADIO_POWER_ON: 2722 return RadioState.RADIO_STATE_ON; 2723 case TelephonyManager.RADIO_POWER_UNAVAILABLE: 2724 return RadioState.RADIO_STATE_UNAVAILABLE; 2725 default: 2726 return RadioState.RADIO_STATE_UNKNOWN; 2727 } 2728 } 2729 2730 /** 2731 * Convert SMS format 2732 */ convertSmsFormat(String format)2733 private int convertSmsFormat(String format) { 2734 int formatCode = SmsSession.Event.Format.SMS_FORMAT_UNKNOWN; 2735 switch (format) { 2736 case SmsMessage.FORMAT_3GPP : { 2737 formatCode = SmsSession.Event.Format.SMS_FORMAT_3GPP; 2738 break; 2739 } 2740 case SmsMessage.FORMAT_3GPP2: { 2741 formatCode = SmsSession.Event.Format.SMS_FORMAT_3GPP2; 2742 break; 2743 } 2744 } 2745 return formatCode; 2746 } 2747 2748 /** 2749 * Get SMS technology 2750 */ getSmsTech(@nboundSmsHandler.SmsSource int smsSource, boolean is3gpp2)2751 private int getSmsTech(@InboundSmsHandler.SmsSource int smsSource, boolean is3gpp2) { 2752 if (smsSource == SOURCE_INJECTED_FROM_IMS) { 2753 return SmsSession.Event.Tech.SMS_IMS; 2754 } else if (smsSource == SOURCE_NOT_INJECTED) { 2755 return is3gpp2 ? SmsSession.Event.Tech.SMS_CDMA : SmsSession.Event.Tech.SMS_GSM; 2756 } else { // SOURCE_INJECTED_FROM_UNKNOWN 2757 return SmsSession.Event.Tech.SMS_UNKNOWN; 2758 } 2759 } 2760 2761 /** 2762 * Convert IMS audio codec into proto defined value 2763 * 2764 * @param c IMS codec value 2765 * @return Codec value defined in call session proto 2766 */ convertImsCodec(int c)2767 private static int convertImsCodec(int c) { 2768 switch (c) { 2769 case ImsStreamMediaProfile.AUDIO_QUALITY_AMR: 2770 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR; 2771 case ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB: 2772 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR_WB; 2773 case ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K: 2774 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_QCELP13K; 2775 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC: 2776 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC; 2777 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B: 2778 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_B; 2779 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB: 2780 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_WB; 2781 case ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW: 2782 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_NW; 2783 case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR: 2784 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_EFR; 2785 case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR: 2786 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_FR; 2787 case ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR: 2788 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_HR; 2789 case ImsStreamMediaProfile.AUDIO_QUALITY_G711U: 2790 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711U; 2791 case ImsStreamMediaProfile.AUDIO_QUALITY_G723: 2792 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G723; 2793 case ImsStreamMediaProfile.AUDIO_QUALITY_G711A: 2794 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711A; 2795 case ImsStreamMediaProfile.AUDIO_QUALITY_G722: 2796 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G722; 2797 case ImsStreamMediaProfile.AUDIO_QUALITY_G711AB: 2798 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G711AB; 2799 case ImsStreamMediaProfile.AUDIO_QUALITY_G729: 2800 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_G729; 2801 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB: 2802 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_NB; 2803 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB: 2804 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_WB; 2805 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB: 2806 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_SWB; 2807 case ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB: 2808 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVS_FB; 2809 default: 2810 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_UNKNOWN; 2811 } 2812 } 2813 2814 /** 2815 * Convert GSM/CDMA audio codec into proto defined value 2816 * 2817 * @param c GSM/CDMA codec value 2818 * @return Codec value defined in call session proto 2819 */ convertGsmCdmaCodec(int c)2820 private int convertGsmCdmaCodec(int c) { 2821 switch (c) { 2822 case DriverCall.AUDIO_QUALITY_AMR: 2823 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR; 2824 case DriverCall.AUDIO_QUALITY_AMR_WB: 2825 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_AMR_WB; 2826 case DriverCall.AUDIO_QUALITY_GSM_EFR: 2827 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_EFR; 2828 case DriverCall.AUDIO_QUALITY_GSM_FR: 2829 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_FR; 2830 case DriverCall.AUDIO_QUALITY_GSM_HR: 2831 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_GSM_HR; 2832 case DriverCall.AUDIO_QUALITY_EVRC: 2833 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC; 2834 case DriverCall.AUDIO_QUALITY_EVRC_B: 2835 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_B; 2836 case DriverCall.AUDIO_QUALITY_EVRC_WB: 2837 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_WB; 2838 case DriverCall.AUDIO_QUALITY_EVRC_NW: 2839 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_EVRC_NW; 2840 default: 2841 return TelephonyCallSession.Event.AudioCodec.AUDIO_CODEC_UNKNOWN; 2842 } 2843 } 2844 2845 /** 2846 * Write audio codec event 2847 * 2848 * @param phoneId Phone id 2849 * @param session IMS call session 2850 */ writeAudioCodecIms(int phoneId, ImsCallSession session)2851 public void writeAudioCodecIms(int phoneId, ImsCallSession session) { 2852 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 2853 if (callSession == null) { 2854 Rlog.e(TAG, "Call session is missing"); 2855 return; 2856 } 2857 2858 ImsCallProfile localCallProfile = session.getLocalCallProfile(); 2859 if (localCallProfile != null) { 2860 int codec = convertImsCodec(localCallProfile.mMediaProfile.mAudioQuality); 2861 callSession.addEvent(new CallSessionEventBuilder( 2862 TelephonyCallSession.Event.Type.AUDIO_CODEC) 2863 .setCallIndex(getCallId(session)) 2864 .setAudioCodec(codec)); 2865 2866 logv("Logged Audio Codec event. Value: " + codec); 2867 } 2868 } 2869 2870 /** 2871 * Write audio codec event 2872 * 2873 * @param phoneId Phone id 2874 * @param audioQuality Audio quality value 2875 */ writeAudioCodecGsmCdma(int phoneId, int audioQuality)2876 public void writeAudioCodecGsmCdma(int phoneId, int audioQuality) { 2877 InProgressCallSession callSession = mInProgressCallSessions.get(phoneId); 2878 if (callSession == null) { 2879 Rlog.e(TAG, "Call session is missing"); 2880 return; 2881 } 2882 2883 int codec = convertGsmCdmaCodec(audioQuality); 2884 callSession.addEvent(new CallSessionEventBuilder( 2885 TelephonyCallSession.Event.Type.AUDIO_CODEC) 2886 .setAudioCodec(codec)); 2887 2888 logv("Logged Audio Codec event. Value: " + codec); 2889 } 2890 2891 //TODO: Expand the proto in the future writeOnImsCallInitiating(int phoneId, ImsCallSession session)2892 public void writeOnImsCallInitiating(int phoneId, ImsCallSession session) {} writeOnImsCallProgressing(int phoneId, ImsCallSession session)2893 public void writeOnImsCallProgressing(int phoneId, ImsCallSession session) {} writeOnImsCallStarted(int phoneId, ImsCallSession session)2894 public void writeOnImsCallStarted(int phoneId, ImsCallSession session) {} writeOnImsCallStartFailed(int phoneId, ImsCallSession session, ImsReasonInfo reasonInfo)2895 public void writeOnImsCallStartFailed(int phoneId, ImsCallSession session, 2896 ImsReasonInfo reasonInfo) {} writeOnImsCallHeld(int phoneId, ImsCallSession session)2897 public void writeOnImsCallHeld(int phoneId, ImsCallSession session) {} writeOnImsCallHoldReceived(int phoneId, ImsCallSession session)2898 public void writeOnImsCallHoldReceived(int phoneId, ImsCallSession session) {} writeOnImsCallHoldFailed(int phoneId, ImsCallSession session, ImsReasonInfo reasonInfo)2899 public void writeOnImsCallHoldFailed(int phoneId, ImsCallSession session, 2900 ImsReasonInfo reasonInfo) {} writeOnImsCallResumed(int phoneId, ImsCallSession session)2901 public void writeOnImsCallResumed(int phoneId, ImsCallSession session) {} writeOnImsCallResumeReceived(int phoneId, ImsCallSession session)2902 public void writeOnImsCallResumeReceived(int phoneId, ImsCallSession session) {} writeOnImsCallResumeFailed(int phoneId, ImsCallSession session, ImsReasonInfo reasonInfo)2903 public void writeOnImsCallResumeFailed(int phoneId, ImsCallSession session, 2904 ImsReasonInfo reasonInfo) {} writeOnRilTimeoutResponse(int phoneId, int rilSerial, int rilRequest)2905 public void writeOnRilTimeoutResponse(int phoneId, int rilSerial, int rilRequest) {} 2906 2907 /** 2908 * Get the sample percentage of collecting metrics based on countries' population. 2909 * 2910 * The larger population the country has, the lower percentage we use to collect this 2911 * metrics. Since the exact population changes frequently, buckets of the population are used 2912 * instead of its exact number. Seven different levels of sampling percentage are assigned 2913 * based on the scale of population for countries. 2914 */ getSamplePercentageForEmergencyCall(String countryIso)2915 private double getSamplePercentageForEmergencyCall(String countryIso) { 2916 String countriesFor1Percentage = "cn,in"; 2917 String countriesFor5Percentage = "us,id,br,pk,ng,bd,ru,mx,jp,et,ph,eg,vn,cd,tr,ir,de"; 2918 String countriesFor15Percentage = "th,gb,fr,tz,it,za,mm,ke,kr,co,es,ug,ar,ua,dz,sd,iq"; 2919 String countriesFor25Percentage = "pl,ca,af,ma,sa,pe,uz,ve,my,ao,mz,gh,np,ye,mg,kp,cm"; 2920 String countriesFor35Percentage = "au,tw,ne,lk,bf,mw,ml,ro,kz,sy,cl,zm,gt,zw,nl,ec,sn"; 2921 String countriesFor45Percentage = "kh,td,so,gn,ss,rw,bj,tn,bi,be,cu,bo,ht,gr,do,cz,pt"; 2922 if (countriesFor1Percentage.contains(countryIso)) { 2923 return 1; 2924 } else if (countriesFor5Percentage.contains(countryIso)) { 2925 return 5; 2926 } else if (countriesFor15Percentage.contains(countryIso)) { 2927 return 15; 2928 } else if (countriesFor25Percentage.contains(countryIso)) { 2929 return 25; 2930 } else if (countriesFor35Percentage.contains(countryIso)) { 2931 return 35; 2932 } else if (countriesFor45Percentage.contains(countryIso)) { 2933 return 45; 2934 } else { 2935 return 50; 2936 } 2937 } 2938 mapSimStateToProto(int simState)2939 private static int mapSimStateToProto(int simState) { 2940 switch (simState) { 2941 case TelephonyManager.SIM_STATE_ABSENT: 2942 return SimState.SIM_STATE_ABSENT; 2943 case TelephonyManager.SIM_STATE_LOADED: 2944 return SimState.SIM_STATE_LOADED; 2945 default: 2946 return SimState.SIM_STATE_UNKNOWN; 2947 } 2948 } 2949 2950 /** 2951 * Write bandwidth estimator stats 2952 */ writeBandwidthStats(int link, int rat, int nrMode, int signalLevel, int bwEstExtErrPercent, int coldStartErrPercent, int bwKbps)2953 public synchronized void writeBandwidthStats(int link, int rat, int nrMode, 2954 int signalLevel, int bwEstExtErrPercent, int coldStartErrPercent, int bwKbps) { 2955 BwEstimationStats stats = lookupEstimationStats(link, rat, nrMode); 2956 stats.mBwEstErrorAcc[signalLevel] += Math.abs(bwEstExtErrPercent); 2957 stats.mStaticBwErrorAcc[signalLevel] += Math.abs(coldStartErrPercent); 2958 stats.mBwAccKbps[signalLevel] += bwKbps; 2959 stats.mCount[signalLevel]++; 2960 } 2961 lookupEstimationStats(int linkIndex, int dataRat, int nrMode)2962 private BwEstimationStats lookupEstimationStats(int linkIndex, int dataRat, int nrMode) { 2963 String dataRatName = LinkBandwidthEstimator.getDataRatName(dataRat, nrMode); 2964 BwEstimationStats ans = mBwEstStatsMapList.get(linkIndex).get(dataRatName); 2965 if (ans == null) { 2966 ans = new BwEstimationStats(dataRat, nrMode); 2967 mBwEstStatsMapList.get(linkIndex).put(dataRatName, ans); 2968 } 2969 return ans; 2970 } 2971 buildBandwidthEstimatorStats()2972 private BandwidthEstimatorStats buildBandwidthEstimatorStats() { 2973 BandwidthEstimatorStats stats = new BandwidthEstimatorStats(); 2974 List<BandwidthEstimatorStats.PerRat> ratList; 2975 ratList = writeBandwidthEstimatorStatsRatList(mBwEstStatsMapList.get(0)); 2976 stats.perRatTx = ratList.toArray(new BandwidthEstimatorStats.PerRat[0]); 2977 ratList = writeBandwidthEstimatorStatsRatList(mBwEstStatsMapList.get(1)); 2978 stats.perRatRx = ratList.toArray(new BandwidthEstimatorStats.PerRat[0]); 2979 return stats; 2980 } 2981 writeBandwidthEstimatorStatsRatList( Map<String, BwEstimationStats> bwEstStatsMap)2982 private List<BandwidthEstimatorStats.PerRat> writeBandwidthEstimatorStatsRatList( 2983 Map<String, BwEstimationStats> bwEstStatsMap) { 2984 List<BandwidthEstimatorStats.PerRat> ratList = new ArrayList<>(); 2985 for (BwEstimationStats perRat : bwEstStatsMap.values()) { 2986 ratList.add(perRat.writeBandwidthStats()); 2987 } 2988 return ratList; 2989 } 2990 2991 private static class BwEstimationStats { 2992 final int mRadioTechnology; 2993 final int mNrMode; 2994 final long[] mBwEstErrorAcc = new long[NUM_SIGNAL_LEVEL]; 2995 final long[] mStaticBwErrorAcc = new long[NUM_SIGNAL_LEVEL]; 2996 final long[] mBwAccKbps = new long[NUM_SIGNAL_LEVEL]; 2997 final int[] mCount = new int[NUM_SIGNAL_LEVEL]; 2998 BwEstimationStats(int radioTechnology, int nrMode)2999 BwEstimationStats(int radioTechnology, int nrMode) { 3000 mRadioTechnology = radioTechnology; 3001 mNrMode = nrMode; 3002 } 3003 3004 @Override toString()3005 public String toString() { 3006 StringBuilder sb = new StringBuilder(); 3007 return sb.append(LinkBandwidthEstimator.getDataRatName(mRadioTechnology, mNrMode)) 3008 .append("\n Count\n").append(printValues(mCount)) 3009 .append("\n AvgKbps\n").append(printAvgValues(mBwAccKbps, mCount)) 3010 .append("\n BwEst Error\n").append(printAvgValues(mBwEstErrorAcc, mCount)) 3011 .append("\n StaticBw Error\n").append(printAvgValues(mStaticBwErrorAcc, mCount)) 3012 .toString(); 3013 } 3014 printValues(int[] values)3015 private String printValues(int[] values) { 3016 StringBuilder sb = new StringBuilder(); 3017 for (int k = 0; k < NUM_SIGNAL_LEVEL; k++) { 3018 sb.append(" " + values[k]); 3019 } 3020 return sb.toString(); 3021 } 3022 printAvgValues(long[] stats, int[] count)3023 private String printAvgValues(long[] stats, int[] count) { 3024 StringBuilder sb = new StringBuilder(); 3025 for (int k = 0; k < NUM_SIGNAL_LEVEL; k++) { 3026 int avgStat = calculateAvg(stats[k], count[k]); 3027 sb.append(" " + avgStat); 3028 } 3029 return sb.toString(); 3030 } 3031 writeBandwidthStats()3032 private BandwidthEstimatorStats.PerRat writeBandwidthStats() { 3033 BandwidthEstimatorStats.PerRat stats = new BandwidthEstimatorStats.PerRat(); 3034 List<BandwidthEstimatorStats.PerLevel> levelList = new ArrayList<>(); 3035 for (int level = 0; level < NUM_SIGNAL_LEVEL; level++) { 3036 BandwidthEstimatorStats.PerLevel currStats = writeBandwidthStatsPerLevel(level); 3037 if (currStats != null) { 3038 levelList.add(currStats); 3039 } 3040 } 3041 stats.rat = mRadioTechnology; 3042 stats.perLevel = levelList.toArray(new BandwidthEstimatorStats.PerLevel[0]); 3043 stats.nrMode = mNrMode; 3044 return stats; 3045 } 3046 writeBandwidthStatsPerLevel(int level)3047 private BandwidthEstimatorStats.PerLevel writeBandwidthStatsPerLevel(int level) { 3048 int count = mCount[level]; 3049 if (count > 0) { 3050 BandwidthEstimatorStats.PerLevel stats = new BandwidthEstimatorStats.PerLevel(); 3051 stats.signalLevel = level; 3052 stats.count = count; 3053 stats.avgBwKbps = calculateAvg(mBwAccKbps[level], count); 3054 stats.staticBwErrorPercent = calculateAvg(mStaticBwErrorAcc[level], count); 3055 stats.bwEstErrorPercent = calculateAvg(mBwEstErrorAcc[level], count); 3056 return stats; 3057 } 3058 return null; 3059 } 3060 calculateAvg(long acc, int count)3061 private int calculateAvg(long acc, int count) { 3062 return (count > 0) ? (int) (acc / count) : 0; 3063 } 3064 } 3065 3066 } 3067