Loading proto/src/persist_atoms.proto +23 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ option java_outer_classname = "PersistAtomsProto"; // Holds atoms to store on persist storage in case of power cycle or process crash. // NOTE: using int64 rather than google.protobuf.Timestamp for timestamps simplifies implementation. // Next id: 5 // Next id: 9 message PersistAtoms { /* Aggregated RAT usage during the call. */ repeated RawVoiceCallRatUsage raw_voice_call_rat_usage = 1; Loading @@ -42,6 +42,12 @@ message PersistAtoms { /* Timestamp of last incoming_sms pull. */ optional int64 incoming_sms_pull_timestamp_millis = 6; /* Outgoing SMS statistics and information. */ repeated OutgoingSms outgoing_sms = 7; /* Timestamp of last incoming_sms pull. */ optional int64 outgoing_sms_pull_timestamp_millis = 8; } // The canonical versions of the following enums live in: Loading Loading @@ -105,3 +111,19 @@ message IncomingSms { optional int32 carrier_id = 13; optional int64 message_id = 14; } message OutgoingSms { optional int32 sms_format = 1; optional int32 sms_tech = 2; optional int32 rat = 3; optional int32 send_result = 4; optional int32 error_code = 5; optional bool is_roaming = 6; optional bool is_from_default_app = 7; optional int32 sim_slot_index = 8; optional bool is_multi_sim = 9; optional bool is_esim = 10; optional int32 carrier_id = 11; optional int64 message_id = 12; optional int32 retry_id = 13; } src/java/com/android/internal/telephony/ImsSmsDispatcher.java +16 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.os.RemoteException; import android.provider.Telephony.Sms.Intents; import android.telephony.CarrierConfigManager; import android.telephony.ServiceState; import android.telephony.SmsManager; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.RegistrationManager; import android.telephony.ims.aidl.IImsSmsListener; Loading Loading @@ -139,7 +140,7 @@ public class ImsSmsDispatcher extends SMSDispatcher { private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() { @Override public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status, int reason, int networkReasonCode) { @SmsManager.Result int reason, int networkReasonCode) { final long identity = Binder.clearCallingIdentity(); try { logd("onSendSmsResult token=" + token + " messageRef=" + messageRef Loading Loading @@ -178,6 +179,13 @@ public class ImsSmsDispatcher extends SMSDispatcher { break; default: } mPhone.getSmsStats().onOutgoingSms( true /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), status == ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK, reason, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } finally { Binder.restoreCallingIdentity(identity); } Loading Loading @@ -436,6 +444,13 @@ public class ImsSmsDispatcher extends SMSDispatcher { fallbackToPstn(tracker); mMetrics.writeImsServiceSendSms(mPhone.getPhoneId(), format, ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK, tracker.mMessageId); mPhone.getSmsStats().onOutgoingSms( true /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(format), true /* fallbackToCs */, SmsManager.RESULT_SYSTEM_ERROR, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } } Loading src/java/com/android/internal/telephony/SMSDispatcher.java +67 −13 Original line number Diff line number Diff line Loading @@ -751,9 +751,10 @@ public abstract class SMSDispatcher extends Handler { protected void handleSendComplete(AsyncResult ar) { SmsTracker tracker = (SmsTracker) ar.userObj; PendingIntent sentIntent = tracker.mSentIntent; SmsResponse smsResponse = (SmsResponse) ar.result; if (ar.result != null) { tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef; if (smsResponse != null) { tracker.mMessageRef = smsResponse.mMessageRef; } else { Rlog.d(TAG, "SmsResponse was null"); } Loading @@ -770,10 +771,17 @@ public abstract class SMSDispatcher extends Handler { } tracker.onSent(mContext); mPhone.notifySmsSent(tracker.mDestAddress); mPhone.getSmsStats().onOutgoingSms( tracker.mImsRetry > 0 /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), false /* fallbackToCs */, SmsManager.RESULT_ERROR_NONE, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } else { if (DBG) { Rlog.d(TAG, "SMS send failed" + " id: " + tracker.mMessageId); Rlog.d(TAG, "SMS send failed id: " + tracker.mMessageId); } int ss = mPhone.getServiceState().getState(); Loading @@ -796,6 +804,13 @@ public abstract class SMSDispatcher extends Handler { // if sms over IMS is not supported on data and voice is not available... if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { tracker.onFailed(mContext, getNotInServiceError(ss), NO_ERROR_CODE); mPhone.getSmsStats().onOutgoingSms( tracker.mImsRetry > 0 /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), false /* fallbackToCs */, getNotInServiceError(ss), tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } else if ((((CommandException)(ar.exception)).getCommandError() == CommandException.Error.SMS_FAIL_RETRY) && tracker.mRetryCount < MAX_SEND_RETRIES) { Loading @@ -808,20 +823,35 @@ public abstract class SMSDispatcher extends Handler { // message, depending on the failure). Also, in some // implementations this retry is handled by the baseband. tracker.mRetryCount++; int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE; Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); mPhone.getSmsStats().onOutgoingSms( tracker.mImsRetry > 0 /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), false /* fallbackToCs */, SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY, errorCode, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } else { int errorCode = NO_ERROR_CODE; if (ar.result != null) { errorCode = ((SmsResponse)ar.result).mErrorCode; } int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE; int error = rilErrorToSmsManagerResult(((CommandException) (ar.exception)) .getCommandError()); tracker.onFailed(mContext, error, errorCode); mPhone.getSmsStats().onOutgoingSms( tracker.mImsRetry > 0 /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), false /* fallbackToCs */, error, errorCode, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } } } @SmsManager.Result private static int rilErrorToSmsManagerResult(CommandException.Error rilError) { switch (rilError) { case RADIO_NOT_AVAILABLE: Loading Loading @@ -881,6 +911,7 @@ public abstract class SMSDispatcher extends Handler { * @param ss service state * @return The result error based on input service state for not in service error */ @SmsManager.Result protected static int getNotInServiceError(int ss) { if (ss == ServiceState.STATE_POWER_OFF) { return RESULT_ERROR_RADIO_OFF; Loading Loading @@ -1294,7 +1325,7 @@ public abstract class SMSDispatcher extends Handler { */ @VisibleForTesting public void sendRawPdu(SmsTracker[] trackers) { int error = RESULT_ERROR_NONE; @SmsManager.Result int error = RESULT_ERROR_NONE; PackageInfo appInfo = null; if (mSmsSendDisabled) { Rlog.e(TAG, "Device does not support sending sms."); Loading Loading @@ -1610,10 +1641,22 @@ public abstract class SMSDispatcher extends Handler { } } private void handleSmsTrackersFailure(SmsTracker[] trackers, int error, int errorCode) { private void handleSmsTrackersFailure(SmsTracker[] trackers, @SmsManager.Result int error, int errorCode) { for (SmsTracker tracker : trackers) { tracker.onFailed(mContext, error, errorCode); } if (trackers.length > 0) { // This error occurs before the SMS is sent. Make an assumption if it would have // been sent over IMS or not. mPhone.getSmsStats().onOutgoingSms( isIms(), SmsConstants.FORMAT_3GPP2.equals(getFormat()), false /* fallbackToCs */, error, trackers[0].mMessageId, trackers[0].isFromDefaultSmsApplication(mContext)); } } /** Loading Loading @@ -1678,6 +1721,8 @@ public abstract class SMSDispatcher extends Handler { public final long mMessageId; private Boolean mIsFromDefaultSmsApplication; // SMS anomaly uuid private final UUID mAnomalyUUID = UUID.fromString("43043600-ea7a-44d2-9ae6-a58567ac7886"); Loading Loading @@ -1725,6 +1770,16 @@ public abstract class SMSDispatcher extends Handler { return mAppInfo != null ? mAppInfo.packageName : null; } /** Return if the SMS was originated from the default SMS application. */ public boolean isFromDefaultSmsApplication(Context context) { if (mIsFromDefaultSmsApplication == null) { // Perform a lazy initialization, due to the cost of the operation. mIsFromDefaultSmsApplication = SmsApplication.isDefaultSmsApplication(context, getAppPackageName()); } return mIsFromDefaultSmsApplication; } /** * Update the status of this message if we persisted it */ Loading Loading @@ -1775,8 +1830,7 @@ public abstract class SMSDispatcher extends Handler { * @return The telephony provider URI if stored */ private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) { if (!mIsText || !mPersistMessage || !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context)) { if (!mIsText || !mPersistMessage || isFromDefaultSmsApplication(context)) { return null; } Rlog.d(TAG, "Persist SMS into " Loading src/java/com/android/internal/telephony/metrics/MetricsCollector.java +36 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS; import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS; import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE; import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY; import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE; Loading @@ -35,6 +36,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms; import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms; import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage; import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession; import com.android.internal.util.ConcurrentUtils; Loading Loading @@ -95,6 +97,7 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { registerAtom(VOICE_CALL_RAT_USAGE, POLICY_PULL_DAILY); registerAtom(VOICE_CALL_SESSION, POLICY_PULL_DAILY); registerAtom(INCOMING_SMS, POLICY_PULL_DAILY); registerAtom(OUTGOING_SMS, POLICY_PULL_DAILY); Rlog.d(TAG, "registered"); } else { Rlog.e(TAG, "could not get StatsManager, atoms not registered"); Loading Loading @@ -127,6 +130,8 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { return pullVoiceCallSessions(data); case INCOMING_SMS: return pullIncomingSms(data); case OUTGOING_SMS: return pullOutgoingSms(data); default: Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag)); return StatsManager.PULL_SKIP; Loading Loading @@ -225,6 +230,18 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { } } private int pullOutgoingSms(List<StatsEvent> data) { OutgoingSms[] smsList = mStorage.getOutgoingSms(MIN_COOLDOWN_MILLIS); if (smsList != null) { // SMS list is already shuffled when SMS were inserted Arrays.stream(smsList).forEach(sms -> data.add(buildStatsEvent(sms))); return StatsManager.PULL_SUCCESS; } else { Rlog.w(TAG, "OUTGOING_SMS pull too frequent, skipping"); return StatsManager.PULL_SKIP; } } /** Registers a pulled atom ID {@code atomId} with optional {@code policy} for pulling. */ private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy) { mStatsManager.setPullAtomCallback(atomId, policy, ConcurrentUtils.DIRECT_EXECUTOR, this); Loading Loading @@ -293,6 +310,25 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { .build(); } private static StatsEvent buildStatsEvent(OutgoingSms sms) { return StatsEvent.newBuilder() .setAtomId(OUTGOING_SMS) .writeInt(sms.smsFormat) .writeInt(sms.smsTech) .writeInt(sms.rat) .writeInt(sms.sendResult) .writeInt(sms.errorCode) .writeBoolean(sms.isRoaming) .writeBoolean(sms.isFromDefaultApp) .writeInt(sms.simSlotIndex) .writeBoolean(sms.isMultiSim) .writeBoolean(sms.isEsim) .writeInt(sms.carrierId) .writeLong(sms.messageId) .writeInt(sms.retryId) .build(); } /** Returns the value rounded to the bucket. */ private static long round(long value, long bucket) { return ((value + bucket / 2) / bucket) * bucket; Loading src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java +70 −27 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.Context; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms; import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms; import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms; import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage; import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession; Loading Loading @@ -70,21 +71,8 @@ public class PersistAtomsStorage { /** Adds a call to the storage. */ public synchronized void addVoiceCallSession(VoiceCallSession call) { int newLength = mAtoms.voiceCallSession.length + 1; if (newLength > MAX_NUM_CALL_SESSIONS) { // will evict one previous call randomly instead of making the array larger newLength = MAX_NUM_CALL_SESSIONS; } else { mAtoms.voiceCallSession = Arrays.copyOf(mAtoms.voiceCallSession, newLength); } int insertAt = 0; if (newLength > 1) { // shuffle when each call is added, or randomly replace a previous call instead if // MAX_NUM_CALL_SESSIONS is reached (call at the last index is evicted). insertAt = sRandom.nextInt(newLength); mAtoms.voiceCallSession[newLength - 1] = mAtoms.voiceCallSession[insertAt]; } mAtoms.voiceCallSession[insertAt] = call; mAtoms.voiceCallSession = insertAtRandomPlace(mAtoms.voiceCallSession, call, MAX_NUM_CALL_SESSIONS); saveAtomsToFile(); } Loading @@ -97,25 +85,52 @@ public class PersistAtomsStorage { /** Adds an incoming SMS to the storage. */ public synchronized void addIncomingSms(IncomingSms sms) { int newLength = mAtoms.incomingSms.length + 1; if (newLength > MAX_NUM_SMS) { mAtoms.incomingSms = insertAtRandomPlace(mAtoms.incomingSms, sms, MAX_NUM_SMS); saveAtomsToFile(); // To be removed Rlog.d(TAG, "Add new incoming SMS atom: " + sms.toString()); } /** Adds an outgoing SMS to the storage. */ public synchronized void addOutgoingSms(OutgoingSms sms) { // Update the retry id, if needed, so that it's unique and larger than all // previous ones. (this algorithm ignores the fact that some SMS atoms might // be dropped due to limit in size of the array). for (OutgoingSms storedSms : mAtoms.outgoingSms) { if (storedSms.messageId == sms.messageId && storedSms.retryId >= sms.retryId) { sms.retryId = storedSms.retryId + 1; } } mAtoms.outgoingSms = insertAtRandomPlace(mAtoms.outgoingSms, sms, MAX_NUM_SMS); saveAtomsToFile(); // To be removed Rlog.d(TAG, "Add new outgoing SMS atom: " + sms.toString()); } /** Inserts a new element in a random position in an array with a maximum size. */ private static <T> T[] insertAtRandomPlace(T[] storage, T instance, int maxLength) { T[] result; int newLength = storage.length + 1; if (newLength > maxLength) { // will evict one previous call randomly instead of making the array larger newLength = MAX_NUM_SMS; newLength = maxLength; result = storage; } else { mAtoms.incomingSms = Arrays.copyOf(mAtoms.incomingSms, newLength); result = Arrays.copyOf(storage, newLength); } int insertAt = 0; if (newLength > 1) { // shuffle when each call is added, or randomly replace a previous call instead if // MAX_NUM_SMS is reached (call at the last index is evicted). // shuffle when each element is added, or randomly replace a previous element instead // if maxLength is reached (entry at the last index is evicted). insertAt = sRandom.nextInt(newLength); mAtoms.incomingSms[newLength - 1] = mAtoms.incomingSms[insertAt]; result[newLength - 1] = result[insertAt]; } mAtoms.incomingSms[insertAt] = sms; saveAtomsToFile(); // To be removed Rlog.d(TAG, "Add new SMS atom: " + sms.toString()); result[insertAt] = instance; return result; } /** Loading Loading @@ -171,6 +186,23 @@ public class PersistAtomsStorage { } } /** * Returns and clears the outgoing SMS if last pulled longer than {@code * minIntervalMillis} ago, otherwise returns {@code null}. */ @Nullable public synchronized OutgoingSms[] getOutgoingSms(long minIntervalMillis) { if (getWallTimeMillis() - mAtoms.outgoingSmsPullTimestampMillis > minIntervalMillis) { mAtoms.outgoingSmsPullTimestampMillis = getWallTimeMillis(); OutgoingSms[] previousOutgoingSms = mAtoms.outgoingSms; mAtoms.outgoingSms = new OutgoingSms[0]; saveAtomsToFile(); return previousOutgoingSms; } else { return null; } } /** Loads {@link PersistAtoms} from a file in private storage. */ private PersistAtoms loadAtomsFromFile() { try { Loading @@ -195,6 +227,13 @@ public class PersistAtomsStorage { atomsFromFile.incomingSms = Arrays.copyOf(atomsFromFile.incomingSms, MAX_NUM_SMS); } if (atomsFromFile.outgoingSms == null) { atomsFromFile.outgoingSms = new OutgoingSms[0]; } if (atomsFromFile.outgoingSms.length > MAX_NUM_SMS) { atomsFromFile.outgoingSms = Arrays.copyOf(atomsFromFile.outgoingSms, MAX_NUM_SMS); } // out of caution, set timestamps to now if they are missing if (atomsFromFile.rawVoiceCallRatUsagePullTimestampMillis == 0L) { atomsFromFile.rawVoiceCallRatUsagePullTimestampMillis = getWallTimeMillis(); Loading @@ -205,6 +244,9 @@ public class PersistAtomsStorage { if (atomsFromFile.incomingSmsPullTimestampMillis == 0L) { atomsFromFile.incomingSmsPullTimestampMillis = getWallTimeMillis(); } if (atomsFromFile.outgoingSmsPullTimestampMillis == 0L) { atomsFromFile.outgoingSmsPullTimestampMillis = getWallTimeMillis(); } return atomsFromFile; } catch (IOException | NullPointerException e) { Rlog.e(TAG, "cannot load/parse PersistAtoms", e); Loading @@ -229,6 +271,7 @@ public class PersistAtomsStorage { atoms.rawVoiceCallRatUsagePullTimestampMillis = currentTime; atoms.voiceCallSessionPullTimestampMillis = currentTime; atoms.incomingSmsPullTimestampMillis = currentTime; atoms.outgoingSmsPullTimestampMillis = currentTime; Rlog.d(TAG, "created new PersistAtoms"); return atoms; } Loading Loading
proto/src/persist_atoms.proto +23 −1 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ option java_outer_classname = "PersistAtomsProto"; // Holds atoms to store on persist storage in case of power cycle or process crash. // NOTE: using int64 rather than google.protobuf.Timestamp for timestamps simplifies implementation. // Next id: 5 // Next id: 9 message PersistAtoms { /* Aggregated RAT usage during the call. */ repeated RawVoiceCallRatUsage raw_voice_call_rat_usage = 1; Loading @@ -42,6 +42,12 @@ message PersistAtoms { /* Timestamp of last incoming_sms pull. */ optional int64 incoming_sms_pull_timestamp_millis = 6; /* Outgoing SMS statistics and information. */ repeated OutgoingSms outgoing_sms = 7; /* Timestamp of last incoming_sms pull. */ optional int64 outgoing_sms_pull_timestamp_millis = 8; } // The canonical versions of the following enums live in: Loading Loading @@ -105,3 +111,19 @@ message IncomingSms { optional int32 carrier_id = 13; optional int64 message_id = 14; } message OutgoingSms { optional int32 sms_format = 1; optional int32 sms_tech = 2; optional int32 rat = 3; optional int32 send_result = 4; optional int32 error_code = 5; optional bool is_roaming = 6; optional bool is_from_default_app = 7; optional int32 sim_slot_index = 8; optional bool is_multi_sim = 9; optional bool is_esim = 10; optional int32 carrier_id = 11; optional int64 message_id = 12; optional int32 retry_id = 13; }
src/java/com/android/internal/telephony/ImsSmsDispatcher.java +16 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.os.RemoteException; import android.provider.Telephony.Sms.Intents; import android.telephony.CarrierConfigManager; import android.telephony.ServiceState; import android.telephony.SmsManager; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.RegistrationManager; import android.telephony.ims.aidl.IImsSmsListener; Loading Loading @@ -139,7 +140,7 @@ public class ImsSmsDispatcher extends SMSDispatcher { private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() { @Override public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status, int reason, int networkReasonCode) { @SmsManager.Result int reason, int networkReasonCode) { final long identity = Binder.clearCallingIdentity(); try { logd("onSendSmsResult token=" + token + " messageRef=" + messageRef Loading Loading @@ -178,6 +179,13 @@ public class ImsSmsDispatcher extends SMSDispatcher { break; default: } mPhone.getSmsStats().onOutgoingSms( true /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), status == ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK, reason, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } finally { Binder.restoreCallingIdentity(identity); } Loading Loading @@ -436,6 +444,13 @@ public class ImsSmsDispatcher extends SMSDispatcher { fallbackToPstn(tracker); mMetrics.writeImsServiceSendSms(mPhone.getPhoneId(), format, ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK, tracker.mMessageId); mPhone.getSmsStats().onOutgoingSms( true /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(format), true /* fallbackToCs */, SmsManager.RESULT_SYSTEM_ERROR, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } } Loading
src/java/com/android/internal/telephony/SMSDispatcher.java +67 −13 Original line number Diff line number Diff line Loading @@ -751,9 +751,10 @@ public abstract class SMSDispatcher extends Handler { protected void handleSendComplete(AsyncResult ar) { SmsTracker tracker = (SmsTracker) ar.userObj; PendingIntent sentIntent = tracker.mSentIntent; SmsResponse smsResponse = (SmsResponse) ar.result; if (ar.result != null) { tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef; if (smsResponse != null) { tracker.mMessageRef = smsResponse.mMessageRef; } else { Rlog.d(TAG, "SmsResponse was null"); } Loading @@ -770,10 +771,17 @@ public abstract class SMSDispatcher extends Handler { } tracker.onSent(mContext); mPhone.notifySmsSent(tracker.mDestAddress); mPhone.getSmsStats().onOutgoingSms( tracker.mImsRetry > 0 /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), false /* fallbackToCs */, SmsManager.RESULT_ERROR_NONE, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } else { if (DBG) { Rlog.d(TAG, "SMS send failed" + " id: " + tracker.mMessageId); Rlog.d(TAG, "SMS send failed id: " + tracker.mMessageId); } int ss = mPhone.getServiceState().getState(); Loading @@ -796,6 +804,13 @@ public abstract class SMSDispatcher extends Handler { // if sms over IMS is not supported on data and voice is not available... if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { tracker.onFailed(mContext, getNotInServiceError(ss), NO_ERROR_CODE); mPhone.getSmsStats().onOutgoingSms( tracker.mImsRetry > 0 /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), false /* fallbackToCs */, getNotInServiceError(ss), tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } else if ((((CommandException)(ar.exception)).getCommandError() == CommandException.Error.SMS_FAIL_RETRY) && tracker.mRetryCount < MAX_SEND_RETRIES) { Loading @@ -808,20 +823,35 @@ public abstract class SMSDispatcher extends Handler { // message, depending on the failure). Also, in some // implementations this retry is handled by the baseband. tracker.mRetryCount++; int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE; Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); mPhone.getSmsStats().onOutgoingSms( tracker.mImsRetry > 0 /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), false /* fallbackToCs */, SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY, errorCode, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } else { int errorCode = NO_ERROR_CODE; if (ar.result != null) { errorCode = ((SmsResponse)ar.result).mErrorCode; } int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE; int error = rilErrorToSmsManagerResult(((CommandException) (ar.exception)) .getCommandError()); tracker.onFailed(mContext, error, errorCode); mPhone.getSmsStats().onOutgoingSms( tracker.mImsRetry > 0 /* isOverIms */, SmsConstants.FORMAT_3GPP2.equals(getFormat()), false /* fallbackToCs */, error, errorCode, tracker.mMessageId, tracker.isFromDefaultSmsApplication(mContext)); } } } @SmsManager.Result private static int rilErrorToSmsManagerResult(CommandException.Error rilError) { switch (rilError) { case RADIO_NOT_AVAILABLE: Loading Loading @@ -881,6 +911,7 @@ public abstract class SMSDispatcher extends Handler { * @param ss service state * @return The result error based on input service state for not in service error */ @SmsManager.Result protected static int getNotInServiceError(int ss) { if (ss == ServiceState.STATE_POWER_OFF) { return RESULT_ERROR_RADIO_OFF; Loading Loading @@ -1294,7 +1325,7 @@ public abstract class SMSDispatcher extends Handler { */ @VisibleForTesting public void sendRawPdu(SmsTracker[] trackers) { int error = RESULT_ERROR_NONE; @SmsManager.Result int error = RESULT_ERROR_NONE; PackageInfo appInfo = null; if (mSmsSendDisabled) { Rlog.e(TAG, "Device does not support sending sms."); Loading Loading @@ -1610,10 +1641,22 @@ public abstract class SMSDispatcher extends Handler { } } private void handleSmsTrackersFailure(SmsTracker[] trackers, int error, int errorCode) { private void handleSmsTrackersFailure(SmsTracker[] trackers, @SmsManager.Result int error, int errorCode) { for (SmsTracker tracker : trackers) { tracker.onFailed(mContext, error, errorCode); } if (trackers.length > 0) { // This error occurs before the SMS is sent. Make an assumption if it would have // been sent over IMS or not. mPhone.getSmsStats().onOutgoingSms( isIms(), SmsConstants.FORMAT_3GPP2.equals(getFormat()), false /* fallbackToCs */, error, trackers[0].mMessageId, trackers[0].isFromDefaultSmsApplication(mContext)); } } /** Loading Loading @@ -1678,6 +1721,8 @@ public abstract class SMSDispatcher extends Handler { public final long mMessageId; private Boolean mIsFromDefaultSmsApplication; // SMS anomaly uuid private final UUID mAnomalyUUID = UUID.fromString("43043600-ea7a-44d2-9ae6-a58567ac7886"); Loading Loading @@ -1725,6 +1770,16 @@ public abstract class SMSDispatcher extends Handler { return mAppInfo != null ? mAppInfo.packageName : null; } /** Return if the SMS was originated from the default SMS application. */ public boolean isFromDefaultSmsApplication(Context context) { if (mIsFromDefaultSmsApplication == null) { // Perform a lazy initialization, due to the cost of the operation. mIsFromDefaultSmsApplication = SmsApplication.isDefaultSmsApplication(context, getAppPackageName()); } return mIsFromDefaultSmsApplication; } /** * Update the status of this message if we persisted it */ Loading Loading @@ -1775,8 +1830,7 @@ public abstract class SMSDispatcher extends Handler { * @return The telephony provider URI if stored */ private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) { if (!mIsText || !mPersistMessage || !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context)) { if (!mIsText || !mPersistMessage || isFromDefaultSmsApplication(context)) { return null; } Rlog.d(TAG, "Persist SMS into " Loading
src/java/com/android/internal/telephony/metrics/MetricsCollector.java +36 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS; import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS; import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE; import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY; import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE; Loading @@ -35,6 +36,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms; import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms; import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage; import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession; import com.android.internal.util.ConcurrentUtils; Loading Loading @@ -95,6 +97,7 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { registerAtom(VOICE_CALL_RAT_USAGE, POLICY_PULL_DAILY); registerAtom(VOICE_CALL_SESSION, POLICY_PULL_DAILY); registerAtom(INCOMING_SMS, POLICY_PULL_DAILY); registerAtom(OUTGOING_SMS, POLICY_PULL_DAILY); Rlog.d(TAG, "registered"); } else { Rlog.e(TAG, "could not get StatsManager, atoms not registered"); Loading Loading @@ -127,6 +130,8 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { return pullVoiceCallSessions(data); case INCOMING_SMS: return pullIncomingSms(data); case OUTGOING_SMS: return pullOutgoingSms(data); default: Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag)); return StatsManager.PULL_SKIP; Loading Loading @@ -225,6 +230,18 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { } } private int pullOutgoingSms(List<StatsEvent> data) { OutgoingSms[] smsList = mStorage.getOutgoingSms(MIN_COOLDOWN_MILLIS); if (smsList != null) { // SMS list is already shuffled when SMS were inserted Arrays.stream(smsList).forEach(sms -> data.add(buildStatsEvent(sms))); return StatsManager.PULL_SUCCESS; } else { Rlog.w(TAG, "OUTGOING_SMS pull too frequent, skipping"); return StatsManager.PULL_SKIP; } } /** Registers a pulled atom ID {@code atomId} with optional {@code policy} for pulling. */ private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy) { mStatsManager.setPullAtomCallback(atomId, policy, ConcurrentUtils.DIRECT_EXECUTOR, this); Loading Loading @@ -293,6 +310,25 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { .build(); } private static StatsEvent buildStatsEvent(OutgoingSms sms) { return StatsEvent.newBuilder() .setAtomId(OUTGOING_SMS) .writeInt(sms.smsFormat) .writeInt(sms.smsTech) .writeInt(sms.rat) .writeInt(sms.sendResult) .writeInt(sms.errorCode) .writeBoolean(sms.isRoaming) .writeBoolean(sms.isFromDefaultApp) .writeInt(sms.simSlotIndex) .writeBoolean(sms.isMultiSim) .writeBoolean(sms.isEsim) .writeInt(sms.carrierId) .writeLong(sms.messageId) .writeInt(sms.retryId) .build(); } /** Returns the value rounded to the bucket. */ private static long round(long value, long bucket) { return ((value + bucket / 2) / bucket) * bucket; Loading
src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java +70 −27 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.content.Context; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms; import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms; import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms; import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage; import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession; Loading Loading @@ -70,21 +71,8 @@ public class PersistAtomsStorage { /** Adds a call to the storage. */ public synchronized void addVoiceCallSession(VoiceCallSession call) { int newLength = mAtoms.voiceCallSession.length + 1; if (newLength > MAX_NUM_CALL_SESSIONS) { // will evict one previous call randomly instead of making the array larger newLength = MAX_NUM_CALL_SESSIONS; } else { mAtoms.voiceCallSession = Arrays.copyOf(mAtoms.voiceCallSession, newLength); } int insertAt = 0; if (newLength > 1) { // shuffle when each call is added, or randomly replace a previous call instead if // MAX_NUM_CALL_SESSIONS is reached (call at the last index is evicted). insertAt = sRandom.nextInt(newLength); mAtoms.voiceCallSession[newLength - 1] = mAtoms.voiceCallSession[insertAt]; } mAtoms.voiceCallSession[insertAt] = call; mAtoms.voiceCallSession = insertAtRandomPlace(mAtoms.voiceCallSession, call, MAX_NUM_CALL_SESSIONS); saveAtomsToFile(); } Loading @@ -97,25 +85,52 @@ public class PersistAtomsStorage { /** Adds an incoming SMS to the storage. */ public synchronized void addIncomingSms(IncomingSms sms) { int newLength = mAtoms.incomingSms.length + 1; if (newLength > MAX_NUM_SMS) { mAtoms.incomingSms = insertAtRandomPlace(mAtoms.incomingSms, sms, MAX_NUM_SMS); saveAtomsToFile(); // To be removed Rlog.d(TAG, "Add new incoming SMS atom: " + sms.toString()); } /** Adds an outgoing SMS to the storage. */ public synchronized void addOutgoingSms(OutgoingSms sms) { // Update the retry id, if needed, so that it's unique and larger than all // previous ones. (this algorithm ignores the fact that some SMS atoms might // be dropped due to limit in size of the array). for (OutgoingSms storedSms : mAtoms.outgoingSms) { if (storedSms.messageId == sms.messageId && storedSms.retryId >= sms.retryId) { sms.retryId = storedSms.retryId + 1; } } mAtoms.outgoingSms = insertAtRandomPlace(mAtoms.outgoingSms, sms, MAX_NUM_SMS); saveAtomsToFile(); // To be removed Rlog.d(TAG, "Add new outgoing SMS atom: " + sms.toString()); } /** Inserts a new element in a random position in an array with a maximum size. */ private static <T> T[] insertAtRandomPlace(T[] storage, T instance, int maxLength) { T[] result; int newLength = storage.length + 1; if (newLength > maxLength) { // will evict one previous call randomly instead of making the array larger newLength = MAX_NUM_SMS; newLength = maxLength; result = storage; } else { mAtoms.incomingSms = Arrays.copyOf(mAtoms.incomingSms, newLength); result = Arrays.copyOf(storage, newLength); } int insertAt = 0; if (newLength > 1) { // shuffle when each call is added, or randomly replace a previous call instead if // MAX_NUM_SMS is reached (call at the last index is evicted). // shuffle when each element is added, or randomly replace a previous element instead // if maxLength is reached (entry at the last index is evicted). insertAt = sRandom.nextInt(newLength); mAtoms.incomingSms[newLength - 1] = mAtoms.incomingSms[insertAt]; result[newLength - 1] = result[insertAt]; } mAtoms.incomingSms[insertAt] = sms; saveAtomsToFile(); // To be removed Rlog.d(TAG, "Add new SMS atom: " + sms.toString()); result[insertAt] = instance; return result; } /** Loading Loading @@ -171,6 +186,23 @@ public class PersistAtomsStorage { } } /** * Returns and clears the outgoing SMS if last pulled longer than {@code * minIntervalMillis} ago, otherwise returns {@code null}. */ @Nullable public synchronized OutgoingSms[] getOutgoingSms(long minIntervalMillis) { if (getWallTimeMillis() - mAtoms.outgoingSmsPullTimestampMillis > minIntervalMillis) { mAtoms.outgoingSmsPullTimestampMillis = getWallTimeMillis(); OutgoingSms[] previousOutgoingSms = mAtoms.outgoingSms; mAtoms.outgoingSms = new OutgoingSms[0]; saveAtomsToFile(); return previousOutgoingSms; } else { return null; } } /** Loads {@link PersistAtoms} from a file in private storage. */ private PersistAtoms loadAtomsFromFile() { try { Loading @@ -195,6 +227,13 @@ public class PersistAtomsStorage { atomsFromFile.incomingSms = Arrays.copyOf(atomsFromFile.incomingSms, MAX_NUM_SMS); } if (atomsFromFile.outgoingSms == null) { atomsFromFile.outgoingSms = new OutgoingSms[0]; } if (atomsFromFile.outgoingSms.length > MAX_NUM_SMS) { atomsFromFile.outgoingSms = Arrays.copyOf(atomsFromFile.outgoingSms, MAX_NUM_SMS); } // out of caution, set timestamps to now if they are missing if (atomsFromFile.rawVoiceCallRatUsagePullTimestampMillis == 0L) { atomsFromFile.rawVoiceCallRatUsagePullTimestampMillis = getWallTimeMillis(); Loading @@ -205,6 +244,9 @@ public class PersistAtomsStorage { if (atomsFromFile.incomingSmsPullTimestampMillis == 0L) { atomsFromFile.incomingSmsPullTimestampMillis = getWallTimeMillis(); } if (atomsFromFile.outgoingSmsPullTimestampMillis == 0L) { atomsFromFile.outgoingSmsPullTimestampMillis = getWallTimeMillis(); } return atomsFromFile; } catch (IOException | NullPointerException e) { Rlog.e(TAG, "cannot load/parse PersistAtoms", e); Loading @@ -229,6 +271,7 @@ public class PersistAtomsStorage { atoms.rawVoiceCallRatUsagePullTimestampMillis = currentTime; atoms.voiceCallSessionPullTimestampMillis = currentTime; atoms.incomingSmsPullTimestampMillis = currentTime; atoms.outgoingSmsPullTimestampMillis = currentTime; Rlog.d(TAG, "created new PersistAtoms"); return atoms; } Loading