• 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.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