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