1 /* 2 * Copyright (C) 2020 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 com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_IMS; 20 import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_UNKNOWN; 21 import static com.android.internal.telephony.InboundSmsHandler.SOURCE_NOT_INJECTED; 22 import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE; 23 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_GENERIC; 24 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED; 25 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY; 26 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_SUCCESS; 27 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP; 28 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP2; 29 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP; 30 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP2; 31 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_IMS; 32 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_UNKNOWN; 33 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_NORMAL; 34 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_SMS_PP; 35 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_VOICEMAIL_INDICATION; 36 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_WAP_PUSH; 37 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_ZERO; 38 import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR; 39 import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK; 40 import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY; 41 import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_SUCCESS; 42 import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_UNKNOWN; 43 44 import android.annotation.Nullable; 45 import android.app.Activity; 46 import android.provider.Telephony.Sms.Intents; 47 import android.telephony.Annotation.NetworkType; 48 import android.telephony.ServiceState; 49 import android.telephony.SmsManager; 50 import android.telephony.TelephonyManager; 51 import android.telephony.ims.stub.ImsRegistrationImplBase; 52 import android.telephony.ims.stub.ImsSmsImplBase; 53 import android.telephony.ims.stub.ImsSmsImplBase.SendStatusResult; 54 55 import com.android.internal.telephony.InboundSmsHandler; 56 import com.android.internal.telephony.Phone; 57 import com.android.internal.telephony.PhoneConstants; 58 import com.android.internal.telephony.PhoneFactory; 59 import com.android.internal.telephony.ServiceStateTracker; 60 import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms; 61 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms; 62 import com.android.telephony.Rlog; 63 64 import java.util.Random; 65 66 /** Collects sms events per phone ID for the pulled atom. */ 67 public class SmsStats { 68 private static final String TAG = SmsStats.class.getSimpleName(); 69 70 /** 3GPP error for out of service: "no network service" in TS 27.005 cl 3.2.5 */ 71 private static final int NO_NETWORK_ERROR_3GPP = 331; 72 73 /** 3GPP2 error for out of service: "Other radio interface problem" in N.S0005 Table 171 */ 74 private static final int NO_NETWORK_ERROR_3GPP2 = 66; 75 76 private final Phone mPhone; 77 78 private final PersistAtomsStorage mAtomsStorage = 79 PhoneFactory.getMetricsCollector().getAtomsStorage(); 80 81 private static final Random RANDOM = new Random(); 82 SmsStats(Phone phone)83 public SmsStats(Phone phone) { 84 mPhone = phone; 85 } 86 87 /** Create a new atom when multi-part incoming SMS is dropped due to missing parts. */ onDroppedIncomingMultipartSms(boolean is3gpp2, int receivedCount, int totalCount)88 public void onDroppedIncomingMultipartSms(boolean is3gpp2, int receivedCount, int totalCount) { 89 IncomingSms proto = getIncomingDefaultProto(is3gpp2, SOURCE_NOT_INJECTED); 90 // Keep SMS tech as unknown because it's possible that it changed overtime and is not 91 // necessarily the current one. Similarly mark the RAT as unknown. 92 proto.smsTech = INCOMING_SMS__SMS_TECH__SMS_TECH_UNKNOWN; 93 proto.rat = TelephonyManager.NETWORK_TYPE_UNKNOWN; 94 proto.error = INCOMING_SMS__ERROR__SMS_ERROR_GENERIC; 95 proto.totalParts = totalCount; 96 proto.receivedParts = receivedCount; 97 mAtomsStorage.addIncomingSms(proto); 98 } 99 100 /** Create a new atom when an SMS for the voicemail indicator is received. */ onIncomingSmsVoicemail(boolean is3gpp2, @InboundSmsHandler.SmsSource int smsSource)101 public void onIncomingSmsVoicemail(boolean is3gpp2, 102 @InboundSmsHandler.SmsSource int smsSource) { 103 IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource); 104 proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_VOICEMAIL_INDICATION; 105 mAtomsStorage.addIncomingSms(proto); 106 } 107 108 /** Create a new atom when an SMS of type zero is received. */ onIncomingSmsTypeZero(@nboundSmsHandler.SmsSource int smsSource)109 public void onIncomingSmsTypeZero(@InboundSmsHandler.SmsSource int smsSource) { 110 IncomingSms proto = getIncomingDefaultProto(false /* is3gpp2 */, smsSource); 111 proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_ZERO; 112 mAtomsStorage.addIncomingSms(proto); 113 } 114 115 /** Create a new atom when an SMS-PP for the SIM card is received. */ onIncomingSmsPP(@nboundSmsHandler.SmsSource int smsSource, boolean success)116 public void onIncomingSmsPP(@InboundSmsHandler.SmsSource int smsSource, boolean success) { 117 IncomingSms proto = getIncomingDefaultProto(false /* is3gpp2 */, smsSource); 118 proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_SMS_PP; 119 proto.error = getIncomingSmsError(success); 120 mAtomsStorage.addIncomingSms(proto); 121 } 122 123 /** Create a new atom when an SMS is received successfully. */ onIncomingSmsSuccess(boolean is3gpp2, @InboundSmsHandler.SmsSource int smsSource, int messageCount, boolean blocked, long messageId)124 public void onIncomingSmsSuccess(boolean is3gpp2, 125 @InboundSmsHandler.SmsSource int smsSource, int messageCount, 126 boolean blocked, long messageId) { 127 IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource); 128 proto.totalParts = messageCount; 129 proto.receivedParts = messageCount; 130 proto.blocked = blocked; 131 proto.messageId = messageId; 132 mAtomsStorage.addIncomingSms(proto); 133 } 134 135 /** Create a new atom when an incoming SMS has an error. */ onIncomingSmsError(boolean is3gpp2, @InboundSmsHandler.SmsSource int smsSource, int result)136 public void onIncomingSmsError(boolean is3gpp2, 137 @InboundSmsHandler.SmsSource int smsSource, int result) { 138 IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource); 139 proto.error = getIncomingSmsError(result); 140 mAtomsStorage.addIncomingSms(proto); 141 } 142 143 /** Create a new atom when an incoming WAP_PUSH SMS is received. */ onIncomingSmsWapPush(@nboundSmsHandler.SmsSource int smsSource, int messageCount, int result, long messageId)144 public void onIncomingSmsWapPush(@InboundSmsHandler.SmsSource int smsSource, 145 int messageCount, int result, long messageId) { 146 IncomingSms proto = getIncomingDefaultProto(false, smsSource); 147 proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_WAP_PUSH; 148 proto.totalParts = messageCount; 149 proto.receivedParts = messageCount; 150 proto.error = getIncomingSmsError(result); 151 proto.messageId = messageId; 152 mAtomsStorage.addIncomingSms(proto); 153 } 154 155 /** Create a new atom when an outgoing SMS is sent. */ onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs, @SmsManager.Result int errorCode, long messageId, boolean isFromDefaultApp)156 public void onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs, 157 @SmsManager.Result int errorCode, long messageId, boolean isFromDefaultApp) { 158 onOutgoingSms(isOverIms, is3gpp2, fallbackToCs, errorCode, NO_ERROR_CODE, 159 messageId, isFromDefaultApp); 160 } 161 162 /** Create a new atom when an outgoing SMS is sent. */ onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs, @SmsManager.Result int errorCode, int radioSpecificErrorCode, long messageId, boolean isFromDefaultApp)163 public void onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs, 164 @SmsManager.Result int errorCode, int radioSpecificErrorCode, long messageId, 165 boolean isFromDefaultApp) { 166 OutgoingSms proto = 167 getOutgoingDefaultProto(is3gpp2, isOverIms, messageId, isFromDefaultApp); 168 169 if (isOverIms) { 170 // Populate error code and result for IMS case 171 proto.errorCode = errorCode; 172 if (fallbackToCs) { 173 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK; 174 } else if (errorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) { 175 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY; 176 } else if (errorCode != SmsManager.RESULT_ERROR_NONE) { 177 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR; 178 } 179 } else { 180 // Populate error code and result for CS case 181 if (errorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) { 182 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY; 183 } else if (errorCode != SmsManager.RESULT_ERROR_NONE) { 184 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR; 185 } 186 proto.errorCode = radioSpecificErrorCode; 187 if (errorCode == SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE 188 && radioSpecificErrorCode == NO_ERROR_CODE) { 189 proto.errorCode = is3gpp2 ? NO_NETWORK_ERROR_3GPP2 : NO_NETWORK_ERROR_3GPP; 190 } 191 } 192 mAtomsStorage.addOutgoingSms(proto); 193 } 194 195 /** Creates a proto for a normal single-part {@code IncomingSms} with default values. */ getIncomingDefaultProto(boolean is3gpp2, @InboundSmsHandler.SmsSource int smsSource)196 private IncomingSms getIncomingDefaultProto(boolean is3gpp2, 197 @InboundSmsHandler.SmsSource int smsSource) { 198 IncomingSms proto = new IncomingSms(); 199 proto.smsFormat = getSmsFormat(is3gpp2); 200 proto.smsTech = getSmsTech(smsSource, is3gpp2); 201 proto.rat = getRat(smsSource); 202 proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_NORMAL; 203 proto.totalParts = 1; 204 proto.receivedParts = 1; 205 proto.blocked = false; 206 proto.error = INCOMING_SMS__ERROR__SMS_SUCCESS; 207 proto.isRoaming = getIsRoaming(); 208 proto.simSlotIndex = getPhoneId(); 209 proto.isMultiSim = SimSlotState.isMultiSim(); 210 proto.isEsim = SimSlotState.isEsim(getPhoneId()); 211 proto.carrierId = getCarrierId(); 212 // Message ID is initialized with random number, as it is not available for all incoming 213 // SMS messages (e.g. those handled by OS or error cases). 214 proto.messageId = RANDOM.nextLong(); 215 return proto; 216 } 217 218 /** Create a proto for a normal {@code OutgoingSms} with default values. */ getOutgoingDefaultProto(boolean is3gpp2, boolean isOverIms, long messageId, boolean isFromDefaultApp)219 private OutgoingSms getOutgoingDefaultProto(boolean is3gpp2, boolean isOverIms, 220 long messageId, boolean isFromDefaultApp) { 221 OutgoingSms proto = new OutgoingSms(); 222 proto.smsFormat = getSmsFormat(is3gpp2); 223 proto.smsTech = getSmsTech(isOverIms, is3gpp2); 224 proto.rat = getRat(isOverIms); 225 proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_SUCCESS; 226 proto.errorCode = isOverIms ? SmsManager.RESULT_ERROR_NONE : NO_ERROR_CODE; 227 proto.isRoaming = getIsRoaming(); 228 proto.isFromDefaultApp = isFromDefaultApp; 229 proto.simSlotIndex = getPhoneId(); 230 proto.isMultiSim = SimSlotState.isMultiSim(); 231 proto.isEsim = SimSlotState.isEsim(getPhoneId()); 232 proto.carrierId = getCarrierId(); 233 // If the message ID is invalid, generate a random value 234 proto.messageId = messageId != 0L ? messageId : RANDOM.nextLong(); 235 // Setting the retry ID to zero. If needed, it will be incremented when the atom is added 236 // in the persistent storage. 237 proto.retryId = 0; 238 return proto; 239 } 240 getSmsFormat(boolean is3gpp2)241 private static int getSmsFormat(boolean is3gpp2) { 242 if (is3gpp2) { 243 return INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP2; 244 } else { 245 return INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP; 246 } 247 } 248 getSmsTech(@nboundSmsHandler.SmsSource int smsSource, boolean is3gpp2)249 private int getSmsTech(@InboundSmsHandler.SmsSource int smsSource, boolean is3gpp2) { 250 if (smsSource == SOURCE_INJECTED_FROM_UNKNOWN) { 251 return INCOMING_SMS__SMS_TECH__SMS_TECH_UNKNOWN; 252 } 253 return getSmsTech(smsSource == SOURCE_INJECTED_FROM_IMS, is3gpp2); 254 } 255 getSmsTech(boolean isOverIms, boolean is3gpp2)256 private int getSmsTech(boolean isOverIms, boolean is3gpp2) { 257 if (isOverIms) { 258 return INCOMING_SMS__SMS_TECH__SMS_TECH_IMS; 259 } else if (is3gpp2) { 260 return INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP2; 261 } else { 262 return INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP; 263 } 264 } 265 getIncomingSmsError(int result)266 private static int getIncomingSmsError(int result) { 267 switch (result) { 268 case Activity.RESULT_OK: 269 case Intents.RESULT_SMS_HANDLED: 270 return INCOMING_SMS__ERROR__SMS_SUCCESS; 271 case Intents.RESULT_SMS_OUT_OF_MEMORY: 272 return INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY; 273 case Intents.RESULT_SMS_UNSUPPORTED: 274 return INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED; 275 case Intents.RESULT_SMS_GENERIC_ERROR: 276 default: 277 return INCOMING_SMS__ERROR__SMS_ERROR_GENERIC; 278 } 279 } 280 getIncomingSmsError(boolean success)281 private static int getIncomingSmsError(boolean success) { 282 if (success) { 283 return INCOMING_SMS__ERROR__SMS_SUCCESS; 284 } else { 285 return INCOMING_SMS__ERROR__SMS_ERROR_GENERIC; 286 } 287 } 288 getOutgoingSmsError(@endStatusResult int imsSendResult)289 private static int getOutgoingSmsError(@SendStatusResult int imsSendResult) { 290 switch (imsSendResult) { 291 case ImsSmsImplBase.SEND_STATUS_OK: 292 return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_SUCCESS; 293 case ImsSmsImplBase.SEND_STATUS_ERROR: 294 return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR; 295 case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY: 296 return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY; 297 case ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK: 298 return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK; 299 default: 300 return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_UNKNOWN; 301 } 302 } 303 getPhoneId()304 private int getPhoneId() { 305 Phone phone = mPhone; 306 if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { 307 phone = mPhone.getDefaultPhone(); 308 } 309 return phone.getPhoneId(); 310 } 311 312 @Nullable getServiceState()313 private ServiceState getServiceState() { 314 Phone phone = mPhone; 315 if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { 316 phone = mPhone.getDefaultPhone(); 317 } 318 ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker(); 319 return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null; 320 } 321 getRat(@nboundSmsHandler.SmsSource int smsSource)322 private @NetworkType int getRat(@InboundSmsHandler.SmsSource int smsSource) { 323 if (smsSource == SOURCE_INJECTED_FROM_UNKNOWN) { 324 return TelephonyManager.NETWORK_TYPE_UNKNOWN; 325 } 326 return getRat(smsSource == SOURCE_INJECTED_FROM_IMS); 327 } 328 getRat(boolean isOverIms)329 private @NetworkType int getRat(boolean isOverIms) { 330 if (isOverIms) { 331 if (mPhone.getImsRegistrationTech() 332 == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) { 333 return TelephonyManager.NETWORK_TYPE_IWLAN; 334 } 335 } 336 // TODO(b/168837897): Returns the RAT at the time the SMS was received.. 337 ServiceState serviceState = getServiceState(); 338 return serviceState != null 339 ? serviceState.getVoiceNetworkType() : TelephonyManager.NETWORK_TYPE_UNKNOWN; 340 } 341 getIsRoaming()342 private boolean getIsRoaming() { 343 ServiceState serviceState = getServiceState(); 344 return serviceState != null ? serviceState.getRoaming() : false; 345 } 346 getCarrierId()347 private int getCarrierId() { 348 Phone phone = mPhone; 349 if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { 350 phone = mPhone.getDefaultPhone(); 351 } 352 return phone.getCarrierId(); 353 } 354 loge(String format, Object... args)355 private void loge(String format, Object... args) { 356 Rlog.e(TAG, "[" + mPhone.getPhoneId() + "]" + String.format(format, args)); 357 } 358 } 359