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