diff options
Diffstat (limited to 'src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java')
-rw-r--r-- | src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java | 1200 |
1 files changed, 1200 insertions, 0 deletions
diff --git a/src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java b/src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java new file mode 100644 index 0000000000..d7a70be565 --- /dev/null +++ b/src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java @@ -0,0 +1,1200 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.analytics; + +import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_IMS; +import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_UNKNOWN; +import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_GENERIC; +import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED; +import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY; +import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_SUCCESS; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.SystemClock; +import android.provider.Settings; +import android.provider.Telephony.Sms.Intents; +import android.telephony.Annotation; +import android.telephony.DisconnectCause; +import android.telephony.ServiceState; +import android.telephony.SmsManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyManager; +import android.telephony.ims.ImsReasonInfo; +import android.telephony.ims.stub.ImsRegistrationImplBase; + +import com.android.internal.telephony.InboundSmsHandler; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.ServiceStateTracker; +import com.android.telephony.Rlog; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Class to handle all telephony analytics related operations Initializes all the Analytics, + * Provider and Util classes. Registers the required Callbacks for supporting the + * ServiceStateAnalytics , SMS Analytics and Call Analytics + */ +public class TelephonyAnalytics { + private static final String TAG = TelephonyAnalytics.class.getSimpleName(); + protected static final int INVALID_SUB_ID = -1; + private final int mSlotIndex; + private final HandlerThread mHandlerThread; + private final Handler mHandler; + private ExecutorService mExecutorService; + protected TelephonyAnalyticsUtil mTelephonyAnalyticsUtil; + protected int mSubId; + protected ServiceStateAnalytics mServiceStateAnalytics; + protected Context mContext; + protected Executor mExecutor; + protected SubscriptionManager mSubscriptionManager; + protected final SubscriptionManager.OnSubscriptionsChangedListener + mSubscriptionsChangeListener = + new SubscriptionManager.OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + int newSubId = getSubId(); + if ((mSubId != newSubId) + && (newSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID)) { + stopAnalytics(mSubId); + mSubId = newSubId; + startAnalytics(newSubId); + Rlog.d( + TAG, + "Started Listener, mSubId = " + + mSubId + + "SlotId = " + + mSlotIndex); + } + } + }; + protected CallAnalyticsProvider mCallAnalyticsProvider; + protected SmsMmsAnalyticsProvider mSmsMmsAnalyticsProvider; + protected ServiceStateAnalyticsProvider mServiceStateAnalyticsProvider; + protected SmsMmsAnalytics mSmsMmsAnalytics; + protected CallAnalytics mCallAnalytics; + protected Phone mPhone; + + public TelephonyAnalytics(Phone phone) { + mPhone = phone; + mContext = mPhone.getContext(); + mExecutor = Runnable::run; + mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + mSlotIndex = mPhone.getPhoneId(); + + mHandlerThread = new HandlerThread(TelephonyAnalytics.class.getSimpleName()); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + mExecutorService = Executors.newSingleThreadExecutor(); + mTelephonyAnalyticsUtil = TelephonyAnalyticsUtil.getInstance(mContext); + initializeAnalyticsClasses(); + mCallAnalyticsProvider = new CallAnalyticsProvider(mTelephonyAnalyticsUtil, mSlotIndex); + mSmsMmsAnalyticsProvider = new SmsMmsAnalyticsProvider(mTelephonyAnalyticsUtil, mSlotIndex); + mServiceStateAnalyticsProvider = + new ServiceStateAnalyticsProvider(mTelephonyAnalyticsUtil, mSlotIndex); + + startAnalytics(mSubId); + + if (mSubscriptionManager != null) { + mSubscriptionManager.addOnSubscriptionsChangedListener( + mExecutor, mSubscriptionsChangeListener); + Rlog.d(TAG, "stopped listener"); + } + } + + @SuppressLint("MissingPermission") + private int getSubId() { + int subId = INVALID_SUB_ID; + try { + SubscriptionInfo info = + mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(mSlotIndex); + subId = info.getSubscriptionId(); + Rlog.d("TelephonyAnalyticsSubId", "SubId = " + subId + + "SlotIndex = " + mSlotIndex); + } catch (NullPointerException e) { + Rlog.e("TelephonyAnalyticsSubId", "Null Pointer Exception Caught"); + } + return subId; + } + + private void initializeAnalyticsClasses() { + mServiceStateAnalytics = new ServiceStateAnalytics(mExecutor); + mSmsMmsAnalytics = new SmsMmsAnalytics(); + mCallAnalytics = new CallAnalytics(); + } + + protected void startAnalytics(int subId) { + if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + Rlog.d( + "StartAnalytics", + "Invalid SubId = " + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + return; + } + mServiceStateAnalytics.registerMyListener(mContext, subId); + } + + protected void stopAnalytics(int subId) { + if (mServiceStateAnalytics != null) { + mServiceStateAnalytics.unregisterMyListener(subId); + } + } + + public SmsMmsAnalytics getSmsMmsAnalytics() { + return mSmsMmsAnalytics; + } + + public CallAnalytics getCallAnalytics() { + return mCallAnalytics; + } + + /** + * Uses the provider class objects,collects the aggregated report from the respective provider + * classes. Dumps the collected stats in the bugreport. + */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + pw.println("+ Telephony Analytics Report [2 months] [Slot ID = " + mSlotIndex + "] +"); + pw.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + pw.println("Call Analytics Summary"); + ArrayList<String> aggregatedCallInfo = mCallAnalyticsProvider.aggregate(); + for (String info : aggregatedCallInfo) { + pw.println("\t" + info); + } + pw.println("-----------------------------------------------"); + pw.println("SMS/MMS Analytics Summary"); + ArrayList<String> aggregatedSmsMmsInfo = mSmsMmsAnalyticsProvider.aggregate(); + for (String info : aggregatedSmsMmsInfo) { + pw.println("\t\t" + info); + } + pw.println("-----------------------------------------------"); + mServiceStateAnalytics.recordCurrentStateBeforeDump(); + pw.println("Service State Analytics Summary "); + ArrayList<String> aggregatedServiceStateInfo = mServiceStateAnalyticsProvider.aggregate(); + for (String info : aggregatedServiceStateInfo) { + pw.println("\t\t" + info); + } + pw.println("-----------------------------------------------"); + } + + /** + * Provides implementation for processing received Call related data. It implements functions to + * handle various scenarios pertaining to Calls. Passes the data to its provider class + * for further processing. + */ + public class CallAnalytics { + private static final String TAG = CallAnalytics.class.getSimpleName(); + + private enum Status { + SUCCESS("Success"), + FAILURE("Failure"); + public String value; + + Status(String value) { + this.value = value; + } + } + + private enum CallType { + NORMAL_CALL("Normal Call"), + SOS_CALL("SOS Call"); + public String value; + + CallType(String value) { + this.value = value; + } + } + + public CallAnalytics() {} + + /** + * Collects and processes data related to calls once the call is terminated. + * + * @param isEmergency : Stores whether the call is an SOS call or not + * @param isOverIms : Stores whether the call is over IMS. + * @param rat : Stores the Radio Access Technology being used when the call ended + * @param simSlotIndex : Sim Slot from which call was operating. + * @param disconnectCause : Reason for call disconnect. + */ + public void onCallTerminated( + boolean isEmergency, + boolean isOverIms, + int rat, + int simSlotIndex, + int disconnectCause) { + String disconnectCauseString; + String status; + String callType; + if (isEmergency) { + callType = CallType.SOS_CALL.value; + } else { + callType = CallType.NORMAL_CALL.value; + } + if (isOverIms) { + disconnectCauseString = sImsCodeMap.get(disconnectCause); + status = + disconnectCause == ImsReasonInfo.CODE_USER_TERMINATED + || disconnectCause + == ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE + ? Status.SUCCESS.value + : Status.FAILURE.value; + } else { + disconnectCauseString = DisconnectCause.toString(disconnectCause); + status = + disconnectCause == DisconnectCause.LOCAL + || disconnectCause == DisconnectCause.NORMAL + ? Status.SUCCESS.value + : Status.FAILURE.value; + } + String ratString = TelephonyManager.getNetworkTypeName(rat); + sendDataToProvider(callType, status, simSlotIndex, rat, ratString, + disconnectCause, disconnectCauseString); + } + + private void sendDataToProvider(String callType, String status, int simSlotIndex, + int rat, String ratString, int disconnectCause, String disconnectCauseString) { + mExecutorService.execute(() -> { + mCallAnalyticsProvider.insertDataToDb( + callType, status, simSlotIndex, ratString, disconnectCauseString); + ArrayList<String> data; + data = + new ArrayList<>( + List.of( + callType, + status, + disconnectCauseString, + "(" + disconnectCause + ")", + ratString, + "(" + rat + ")", + Integer.toString(simSlotIndex))); + Rlog.d(TAG, data.toString()); + }); + } + + private static final Map<Integer, String> sImsCodeMap; + + static { + sImsCodeMap = new HashMap<>(); + sImsCodeMap.put(ImsReasonInfo.CODE_UNSPECIFIED, "CODE_UNSPECIFIED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT, "CODE_LOCAL_ILLEGAL_ARGUMENT"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE, "CODE_LOCAL_ILLEGAL_STATE"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR, "CODE_LOCAL_INTERNAL_ERROR"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN, "CODE_LOCAL_IMS_SERVICE_DOWN"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL, "CODE_LOCAL_NO_PENDING_CALL"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE, + "CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_POWER_OFF, "CODE_LOCAL_POWER_OFF"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, "CODE_LOCAL_LOW_BATTERY"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE, "CODE_LOCAL_NETWORK_NO_SERVICE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE, + "CODE_LOCAL_NETWORK_NO_LTE_COVERAGE"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING, "CODE_LOCAL_NETWORK_ROAMING"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED, "CODE_LOCAL_NETWORK_IP_CHANGED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE, "CODE_LOCAL_SERVICE_UNAVAILABLE"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, "CODE_LOCAL_NOT_REGISTERED"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_CALL_EXCEEDED, "CODE_LOCAL_CALL_EXCEEDED"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_CALL_BUSY, "CODE_LOCAL_CALL_BUSY"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_CALL_DECLINE, "CODE_LOCAL_CALL_DECLINE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING, + "CODE_LOCAL_CALL_VCC_ON_PROGRESSING"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED, + "CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED, + "CODE_LOCAL_CALL_CS_RETRY_REQUIRED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED, + "CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED, "CODE_LOCAL_CALL_TERMINATED"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE, "CODE_LOCAL_HO_NOT_FEASIBLE"); + sImsCodeMap.put(ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING, "CODE_TIMEOUT_1XX_WAITING"); + sImsCodeMap.put(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER, "CODE_TIMEOUT_NO_ANSWER"); + sImsCodeMap.put( + ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE, + "CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE"); + sImsCodeMap.put(ImsReasonInfo.CODE_CALL_BARRED, "CODE_CALL_BARRED"); + sImsCodeMap.put(ImsReasonInfo.CODE_FDN_BLOCKED, "CODE_FDN_BLOCKED"); + sImsCodeMap.put(ImsReasonInfo.CODE_IMEI_NOT_ACCEPTED, "CODE_IMEI_NOT_ACCEPTED"); + sImsCodeMap.put(ImsReasonInfo.CODE_DIAL_MODIFIED_TO_USSD, "CODE_DIAL_MODIFIED_TO_USSD"); + sImsCodeMap.put(ImsReasonInfo.CODE_DIAL_MODIFIED_TO_SS, "CODE_DIAL_MODIFIED_TO_SS"); + sImsCodeMap.put(ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL, "CODE_DIAL_MODIFIED_TO_DIAL"); + sImsCodeMap.put( + ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL_VIDEO, + "CODE_DIAL_MODIFIED_TO_DIAL_VIDEO"); + sImsCodeMap.put( + ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL, + "CODE_DIAL_VIDEO_MODIFIED_TO_DIAL"); + sImsCodeMap.put( + ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO, + "CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO"); + sImsCodeMap.put( + ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_SS, "CODE_DIAL_VIDEO_MODIFIED_TO_SS"); + sImsCodeMap.put( + ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_USSD, + "CODE_DIAL_VIDEO_MODIFIED_TO_USSD"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_REDIRECTED, "CODE_SIP_REDIRECTED"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_BAD_REQUEST, "CODE_SIP_BAD_REQUEST"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_FORBIDDEN, "CODE_SIP_FORBIDDEN"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_NOT_FOUND, "CODE_SIP_NOT_FOUND"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_NOT_SUPPORTED, "CODE_SIP_NOT_SUPPORTED"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT, "CODE_SIP_REQUEST_TIMEOUT"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE, + "CODE_SIP_TEMPRARILY_UNAVAILABLE"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_BAD_ADDRESS, "CODE_SIP_BAD_ADDRESS"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_BUSY, "CODE_SIP_BUSY"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED, "CODE_SIP_REQUEST_CANCELLED"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE, "CODE_SIP_NOT_ACCEPTABLE"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_NOT_REACHABLE, "CODE_SIP_NOT_REACHABLE"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_CLIENT_ERROR, "CODE_SIP_CLIENT_ERROR"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_TRANSACTION_DOES_NOT_EXIST, + "CODE_SIP_TRANSACTION_DOES_NOT_EXIST"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_SERVER_INTERNAL_ERROR, "CODE_SIP_SERVER_INTERNAL_ERROR"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE, "CODE_SIP_SERVICE_UNAVAILABLE"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_SERVER_TIMEOUT, "CODE_SIP_SERVER_TIMEOUT"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_SERVER_ERROR, "CODE_SIP_SERVER_ERROR"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_USER_REJECTED, "CODE_SIP_USER_REJECTED"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_GLOBAL_ERROR, "CODE_SIP_GLOBAL_ERROR"); + sImsCodeMap.put( + ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE, "CODE_EMERGENCY_TEMP_FAILURE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE, "CODE_EMERGENCY_PERM_FAILURE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_USER_MARKED_UNWANTED, "CODE_SIP_USER_MARKED_UNWANTED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_METHOD_NOT_ALLOWED, "CODE_SIP_METHOD_NOT_ALLOWED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_PROXY_AUTHENTICATION_REQUIRED, + "CODE_SIP_PROXY_AUTHENTICATION_REQUIRED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_REQUEST_ENTITY_TOO_LARGE, + "CODE_SIP_REQUEST_ENTITY_TOO_LARGE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_REQUEST_URI_TOO_LARGE, "CODE_SIP_REQUEST_URI_TOO_LARGE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_EXTENSION_REQUIRED, "CODE_SIP_EXTENSION_REQUIRED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_INTERVAL_TOO_BRIEF, "CODE_SIP_INTERVAL_TOO_BRIEF"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST, + "CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_LOOP_DETECTED, "CODE_SIP_LOOP_DETECTED"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_TOO_MANY_HOPS, "CODE_SIP_TOO_MANY_HOPS"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_AMBIGUOUS, "CODE_SIP_AMBIGUOUS"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_REQUEST_PENDING, "CODE_SIP_REQUEST_PENDING"); + sImsCodeMap.put(ImsReasonInfo.CODE_SIP_UNDECIPHERABLE, "CODE_SIP_UNDECIPHERABLE"); + sImsCodeMap.put(ImsReasonInfo.CODE_MEDIA_INIT_FAILED, "CODE_MEDIA_INIT_FAILED"); + sImsCodeMap.put(ImsReasonInfo.CODE_MEDIA_NO_DATA, "CODE_MEDIA_NO_DATA"); + sImsCodeMap.put(ImsReasonInfo.CODE_MEDIA_NOT_ACCEPTABLE, "CODE_MEDIA_NOT_ACCEPTABLE"); + sImsCodeMap.put(ImsReasonInfo.CODE_MEDIA_UNSPECIFIED, "CODE_MEDIA_UNSPECIFIED"); + sImsCodeMap.put(ImsReasonInfo.CODE_USER_TERMINATED, "CODE_USER_TERMINATED"); + sImsCodeMap.put(ImsReasonInfo.CODE_USER_NOANSWER, "CODE_USER_NOANSWER"); + sImsCodeMap.put(ImsReasonInfo.CODE_USER_IGNORE, "CODE_USER_IGNORE"); + sImsCodeMap.put(ImsReasonInfo.CODE_USER_DECLINE, "CODE_USER_DECLINE"); + sImsCodeMap.put(ImsReasonInfo.CODE_LOW_BATTERY, "CODE_LOW_BATTERY"); + sImsCodeMap.put(ImsReasonInfo.CODE_BLACKLISTED_CALL_ID, "CODE_BLACKLISTED_CALL_ID"); + sImsCodeMap.put( + ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, "CODE_USER_TERMINATED_BY_REMOTE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_USER_REJECTED_SESSION_MODIFICATION, + "CODE_USER_REJECTED_SESSION_MODIFICATION"); + sImsCodeMap.put( + ImsReasonInfo.CODE_USER_CANCELLED_SESSION_MODIFICATION, + "CODE_USER_CANCELLED_SESSION_MODIFICATION"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SESSION_MODIFICATION_FAILED, + "CODE_SESSION_MODIFICATION_FAILED"); + sImsCodeMap.put(ImsReasonInfo.CODE_UT_NOT_SUPPORTED, "CODE_UT_NOT_SUPPORTED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE, "CODE_UT_SERVICE_UNAVAILABLE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_UT_OPERATION_NOT_ALLOWED, "CODE_UT_OPERATION_NOT_ALLOWED"); + sImsCodeMap.put(ImsReasonInfo.CODE_UT_NETWORK_ERROR, "CODE_UT_NETWORK_ERROR"); + sImsCodeMap.put( + ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH, "CODE_UT_CB_PASSWORD_MISMATCH"); + sImsCodeMap.put( + ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL, "CODE_UT_SS_MODIFIED_TO_DIAL"); + sImsCodeMap.put( + ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD, "CODE_UT_SS_MODIFIED_TO_USSD"); + sImsCodeMap.put(ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS, "CODE_UT_SS_MODIFIED_TO_SS"); + sImsCodeMap.put( + ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO, + "CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO"); + sImsCodeMap.put(ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED, "CODE_ECBM_NOT_SUPPORTED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED, + "CODE_MULTIENDPOINT_NOT_SUPPORTED"); + sImsCodeMap.put(ImsReasonInfo.CODE_REGISTRATION_ERROR, "CODE_REGISTRATION_ERROR"); + sImsCodeMap.put(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE, "CODE_ANSWERED_ELSEWHERE"); + sImsCodeMap.put(ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC, "CODE_CALL_PULL_OUT_OF_SYNC"); + sImsCodeMap.put( + ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL, "CODE_CALL_END_CAUSE_CALL_PULL"); + sImsCodeMap.put( + ImsReasonInfo.CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE, + "CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE"); + sImsCodeMap.put(ImsReasonInfo.CODE_REJECTED_ELSEWHERE, "CODE_REJECTED_ELSEWHERE"); + sImsCodeMap.put(ImsReasonInfo.CODE_SUPP_SVC_FAILED, "CODE_SUPP_SVC_FAILED"); + sImsCodeMap.put(ImsReasonInfo.CODE_SUPP_SVC_CANCELLED, "CODE_SUPP_SVC_CANCELLED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SUPP_SVC_REINVITE_COLLISION, + "CODE_SUPP_SVC_REINVITE_COLLISION"); + sImsCodeMap.put(ImsReasonInfo.CODE_IWLAN_DPD_FAILURE, "CODE_IWLAN_DPD_FAILURE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_EPDG_TUNNEL_ESTABLISH_FAILURE, + "CODE_EPDG_TUNNEL_ESTABLISH_FAILURE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_EPDG_TUNNEL_REKEY_FAILURE, "CODE_EPDG_TUNNEL_REKEY_FAILURE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_EPDG_TUNNEL_LOST_CONNECTION, + "CODE_EPDG_TUNNEL_LOST_CONNECTION"); + sImsCodeMap.put( + ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED, + "CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED"); + sImsCodeMap.put(ImsReasonInfo.CODE_REMOTE_CALL_DECLINE, "CODE_REMOTE_CALL_DECLINE"); + sImsCodeMap.put(ImsReasonInfo.CODE_DATA_LIMIT_REACHED, "CODE_DATA_LIMIT_REACHED"); + sImsCodeMap.put(ImsReasonInfo.CODE_DATA_DISABLED, "CODE_DATA_DISABLED"); + sImsCodeMap.put(ImsReasonInfo.CODE_WIFI_LOST, "CODE_WIFI_LOST"); + sImsCodeMap.put(ImsReasonInfo.CODE_IKEV2_AUTH_FAILURE, "CODE_IKEV2_AUTH_FAILURE"); + sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_OFF, "CODE_RADIO_OFF"); + sImsCodeMap.put(ImsReasonInfo.CODE_NO_VALID_SIM, "CODE_NO_VALID_SIM"); + sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_INTERNAL_ERROR, "CODE_RADIO_INTERNAL_ERROR"); + sImsCodeMap.put(ImsReasonInfo.CODE_NETWORK_RESP_TIMEOUT, "CODE_NETWORK_RESP_TIMEOUT"); + sImsCodeMap.put(ImsReasonInfo.CODE_NETWORK_REJECT, "CODE_NETWORK_REJECT"); + sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_ACCESS_FAILURE, "CODE_RADIO_ACCESS_FAILURE"); + sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_LINK_FAILURE, "CODE_RADIO_LINK_FAILURE"); + sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_LINK_LOST, "CODE_RADIO_LINK_LOST"); + sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_UPLINK_FAILURE, "CODE_RADIO_UPLINK_FAILURE"); + sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_SETUP_FAILURE, "CODE_RADIO_SETUP_FAILURE"); + sImsCodeMap.put(ImsReasonInfo.CODE_RADIO_RELEASE_NORMAL, "CODE_RADIO_RELEASE_NORMAL"); + sImsCodeMap.put( + ImsReasonInfo.CODE_RADIO_RELEASE_ABNORMAL, "CODE_RADIO_RELEASE_ABNORMAL"); + sImsCodeMap.put(ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED, "CODE_ACCESS_CLASS_BLOCKED"); + sImsCodeMap.put(ImsReasonInfo.CODE_NETWORK_DETACH, "CODE_NETWORK_DETACH"); + sImsCodeMap.put( + ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL, + "CODE_SIP_ALTERNATE_EMERGENCY_CALL"); + sImsCodeMap.put(ImsReasonInfo.CODE_UNOBTAINABLE_NUMBER, "CODE_UNOBTAINABLE_NUMBER"); + sImsCodeMap.put(ImsReasonInfo.CODE_NO_CSFB_IN_CS_ROAM, "CODE_NO_CSFB_IN_CS_ROAM"); + sImsCodeMap.put(ImsReasonInfo.CODE_REJECT_UNKNOWN, "CODE_REJECT_UNKNOWN"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_ONGOING_CALL_WAITING_DISABLED, + "CODE_REJECT_ONGOING_CALL_WAITING_DISABLED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_CALL_ON_OTHER_SUB, "CODE_REJECT_CALL_ON_OTHER_SUB"); + sImsCodeMap.put(ImsReasonInfo.CODE_REJECT_1X_COLLISION, "CODE_REJECT_1X_COLLISION"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_SERVICE_NOT_REGISTERED, + "CODE_REJECT_SERVICE_NOT_REGISTERED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_CALL_TYPE_NOT_ALLOWED, + "CODE_REJECT_CALL_TYPE_NOT_ALLOWED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_ONGOING_E911_CALL, "CODE_REJECT_ONGOING_E911_CALL"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_ONGOING_CALL_SETUP, "CODE_REJECT_ONGOING_CALL_SETUP"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_MAX_CALL_LIMIT_REACHED, + "CODE_REJECT_MAX_CALL_LIMIT_REACHED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_UNSUPPORTED_SIP_HEADERS, + "CODE_REJECT_UNSUPPORTED_SIP_HEADERS"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_UNSUPPORTED_SDP_HEADERS, + "CODE_REJECT_UNSUPPORTED_SDP_HEADERS"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_ONGOING_CALL_TRANSFER, + "CODE_REJECT_ONGOING_CALL_TRANSFER"); + sImsCodeMap.put(ImsReasonInfo.CODE_REJECT_INTERNAL_ERROR, "CODE_REJECT_INTERNAL_ERROR"); + sImsCodeMap.put(ImsReasonInfo.CODE_REJECT_QOS_FAILURE, "CODE_REJECT_QOS_FAILURE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_ONGOING_HANDOVER, "CODE_REJECT_ONGOING_HANDOVER"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_VT_TTY_NOT_ALLOWED, "CODE_REJECT_VT_TTY_NOT_ALLOWED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_ONGOING_CALL_UPGRADE, + "CODE_REJECT_ONGOING_CALL_UPGRADE"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED, + "CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_ONGOING_CONFERENCE_CALL, + "CODE_REJECT_ONGOING_CONFERENCE_CALL"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_VT_AVPF_NOT_ALLOWED, + "CODE_REJECT_VT_AVPF_NOT_ALLOWED"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_ONGOING_ENCRYPTED_CALL, + "CODE_REJECT_ONGOING_ENCRYPTED_CALL"); + sImsCodeMap.put( + ImsReasonInfo.CODE_REJECT_ONGOING_CS_CALL, "CODE_REJECT_ONGOING_CS_CALL"); + sImsCodeMap.put(ImsReasonInfo.CODE_NETWORK_CONGESTION, "CODE_NETWORK_CONGESTION"); + sImsCodeMap.put( + ImsReasonInfo.CODE_RETRY_ON_IMS_WITHOUT_RTT, "CODE_RETRY_ON_IMS_WITHOUT_RTT"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_1, "CODE_OEM_CAUSE_1"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_2, "CODE_OEM_CAUSE_2"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_3, "CODE_OEM_CAUSE_3"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_4, "CODE_OEM_CAUSE_4"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_5, "CODE_OEM_CAUSE_5"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_6, "CODE_OEM_CAUSE_6"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_7, "CODE_OEM_CAUSE_7"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_8, "CODE_OEM_CAUSE_8"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_9, "CODE_OEM_CAUSE_9"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_10, "CODE_OEM_CAUSE_10"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_11, "CODE_OEM_CAUSE_11"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_12, "CODE_OEM_CAUSE_12"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_13, "CODE_OEM_CAUSE_13"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_14, "CODE_OEM_CAUSE_14"); + sImsCodeMap.put(ImsReasonInfo.CODE_OEM_CAUSE_15, "CODE_OEM_CAUSE_15"); + } + } + + /** + * Implements and Registers the required Listeners and BroadcastReceivers for receiving + * ServiceState related information. Performs required logic on received data and then Passes + * the information to its provider class for further processing. + */ + public class ServiceStateAnalytics extends TelephonyCallback + implements TelephonyCallback.ServiceStateListener { + private final Executor mExecutor; + private static final String TAG = ServiceStateAnalytics.class.getSimpleName(); + private static final int BUFFER_TIME = 10000; + + private TelephonyManager mTelephonyManager; + + private enum DeviceStatus { + APM, + CELLULAR_OOS_WITH_IWLAN, + NO_NETWORK_COVERAGE, + SIM_ABSENT, + IN_SERVICE; + } + + private final AtomicReference<TimeStampedServiceState> mLastState = + new AtomicReference<>(null); + private static final String NA = "NA"; + private final BroadcastReceiver mBroadcastReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final long now = getTimeMillis(); + if (intent.getAction() + .equals(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)) { + int simState = + intent.getIntExtra( + TelephonyManager.EXTRA_SIM_STATE, + TelephonyManager.SIM_STATE_UNKNOWN); + if (simState == TelephonyManager.SIM_STATE_ABSENT) { + Rlog.d("AnkitSimAbsent", "Sim is Absent"); + logSimAbsentState(); + } + } + } + }; + + protected ServiceStateAnalytics(Executor executor) { + super(); + mExecutor = executor; + IntentFilter mIntentFilter = + new IntentFilter(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED); + mContext.registerReceiver(mBroadcastReceiver, mIntentFilter); + } + + @Override + public void onServiceStateChanged(@NonNull ServiceState serviceState) { + int dataRegState = serviceState.getDataRegState(); + int voiceRegState = serviceState.getVoiceRegState(); + int voiceRadioTechnology = serviceState.getRilVoiceRadioTechnology(); + int dataRadioTechnology = serviceState.getRilDataRadioTechnology(); + + mExecutorService.execute(() -> { + logServiceState(dataRegState, voiceRegState, voiceRadioTechnology, + dataRadioTechnology); + }); + } + + private void logServiceState( + int dataRegState, + int voiceRegState, + int voiceRadioTechnology, + int dataRadioTechnology) { + long now = getTimeMillis(); + String voiceRadioTechnologyName = + ServiceState.rilRadioTechnologyToString(voiceRadioTechnology); + String dataRadioTechnologyName = + ServiceState.rilRadioTechnologyToString(dataRadioTechnology); + + if (isAirplaneModeOn()) { + if (dataRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN + && dataRegState == ServiceState.STATE_IN_SERVICE) { + logOosWithIwlan(now); + } else { + logAirplaneModeServiceState(now); + } + } else { + if (voiceRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN + && dataRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) { + logNoNetworkCoverage(now); + + } else if (voiceRadioTechnology != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN + && dataRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) { + if (voiceRegState == ServiceState.STATE_IN_SERVICE) { + logInServiceData(voiceRadioTechnologyName, now); + } else { + logNoNetworkCoverage(now); + } + } else if (voiceRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) { + if (dataRegState == ServiceState.STATE_IN_SERVICE) { + if (dataRadioTechnology == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) { + logOosWithIwlan(now); + } else { + logInServiceData(dataRadioTechnologyName, now); + } + } else { + logNoNetworkCoverage(now); + } + } else { + if (dataRegState == ServiceState.STATE_IN_SERVICE + || voiceRegState == ServiceState.STATE_IN_SERVICE) { + logInServiceData(voiceRadioTechnologyName, now); + } else { + logNoNetworkCoverage(now); + } + } + } + } + + private void logSimAbsentState() { + long now = getTimeMillis(); + TimeStampedServiceState currentState = + new TimeStampedServiceState( + mSlotIndex, NA, DeviceStatus.SIM_ABSENT.name(), now); + setCurrentStateAndAddLastState(currentState, now); + } + private void logOosWithIwlan(long now) { + TimeStampedServiceState currentState = + new TimeStampedServiceState(mSlotIndex, NA, + DeviceStatus.CELLULAR_OOS_WITH_IWLAN.name(), now); + setCurrentStateAndAddLastState(currentState, now); + } + + private void logAirplaneModeServiceState(long now) { + TimeStampedServiceState currentState = + new TimeStampedServiceState(mSlotIndex, NA, DeviceStatus.APM.name(), now); + setCurrentStateAndAddLastState(currentState, now); + } + + private void logNoNetworkCoverage(long now) { + TimeStampedServiceState currentState = + new TimeStampedServiceState( + mSlotIndex, NA, DeviceStatus.NO_NETWORK_COVERAGE.name(), now); + setCurrentStateAndAddLastState(currentState, now); + } + + private void logInServiceData(String rat, long now) { + TimeStampedServiceState currentState = + new TimeStampedServiceState( + mSlotIndex, rat, DeviceStatus.IN_SERVICE.name(), now); + setCurrentStateAndAddLastState(currentState, now); + } + + private void setCurrentStateAndAddLastState( + TimeStampedServiceState currentState, long now) { + TimeStampedServiceState lastState = mLastState.getAndSet(currentState); + addData(lastState, now); + } + + private void addData(TimeStampedServiceState lastState, long now) { + if (lastState == null) { + return; + } + if (now - lastState.mTimestampStart < BUFFER_TIME) { + return; + } + Rlog.d(TAG, "Last State = " + lastState.toString() + "End = " + now); + mServiceStateAnalyticsProvider.insertDataToDb(lastState, now); + } + + private void recordCurrentStateBeforeDump() { + long now = getTimeMillis(); + Rlog.d("RecordingStateBDump", "Recording " + now); + TimeStampedServiceState currentState = mLastState.get(); + mLastState.set(createCopyWithUpdatedTimestamp(currentState)); + addData(currentState, now); + } + + private TimeStampedServiceState createCopyWithUpdatedTimestamp( + TimeStampedServiceState currentState) { + if (currentState == null) { + return null; + } + long now = getTimeMillis(); + TimeStampedServiceState state = + new TimeStampedServiceState( + currentState.mSlotIndex, + currentState.mRAT, + currentState.mDeviceStatus, + now); + return state; + } + + private boolean isAirplaneModeOn() { + return Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) + != 0; + } + + protected long getTimeMillis() { + return SystemClock.elapsedRealtime(); + } + + void registerMyListener(Context context, int subId) { + try { + mTelephonyManager = + context.getSystemService(TelephonyManager.class) + .createForSubscriptionId(subId); + mTelephonyManager.registerTelephonyCallback(mExecutor, this); + + } catch (NullPointerException e) { + log("Null pointer exception caught " + e); + } + } + + void unregisterMyListener(int subId) { + mTelephonyManager.unregisterTelephonyCallback(this); + } + + private void log(String s) { + Rlog.d(ServiceStateAnalytics.class.getSimpleName(), s); + } + + /** + * Serves the functionality of storing service state related information, + * Along with the timestamp at which the state was detected. + */ + public static class TimeStampedServiceState { + protected final int mSlotIndex; + protected final String mRAT; + protected final String mDeviceStatus; + protected final long mTimestampStart; + + public TimeStampedServiceState( + int slotIndex, String rat, String deviceStatus, long timestampStart) { + mSlotIndex = slotIndex; + mRAT = rat; + mDeviceStatus = deviceStatus; + mTimestampStart = timestampStart; + } + + @Override + public String toString() { + return "SlotIndex = " + + mSlotIndex + + " RAT = " + + mRAT + + " DeviceStatus = " + + mDeviceStatus + + "TimeStampStart = " + + mTimestampStart; + } + /** Getter function for slotIndex */ + public int getSlotIndex() { + return mSlotIndex; + } + + /** Getter function for state start Timestamp */ + public long getTimestampStart() { + return mTimestampStart; + } + + /** Getter function for device Status */ + public String getDeviceStatus() { + return mDeviceStatus; + } + + /** Getter function for radio access technology */ + public String getRAT() { + return mRAT; + } + } + } + + /** + * Provides implementation for processing received Sms related data. Implements functions to + * handle various scenarios pertaining to Sms. Passes the data to its provider for further + * processing. + */ + public class SmsMmsAnalytics { + private static final String TAG = SmsMmsAnalytics.class.getSimpleName(); + public SmsMmsAnalytics() { + + } + + /** Collects Outgoing Sms related information. */ + public void onOutgoingSms(boolean isOverIms, @SmsManager.Result int sendErrorCode) { + Rlog.d( + TAG, + "Is Over Ims = " + + isOverIms + + " sendErrorCode = " + + sendErrorCode + + "SlotInfo =" + + mSlotIndex); + logOutgoingSms(isOverIms, sendErrorCode); + } + + /** Collects Successful Incoming Sms related information. */ + public void onIncomingSmsSuccess(@InboundSmsHandler.SmsSource int smsSource) { + Rlog.d(TAG, " smsSource = " + smsSource); + String status = "Success"; + String failureReason = "NA"; + logIncomingSms(smsSource, status, failureReason); + } + + /** Collects Failed Incoming Multipart Sms related information. */ + public void onDroppedIncomingMultipartSms() { + String status = "Failure"; + String type = "SMS Incoming"; + // Mark the RAT as unknown since it might have changed over time. + int rat = TelephonyManager.NETWORK_TYPE_UNKNOWN; + String ratString = ServiceState.rilRadioTechnologyToString(rat); + String failureReason = "INCOMING_SMS__ERROR__SMS_ERROR_GENERIC"; + sendDataToProvider(status, type, ratString, failureReason); + } + + /** Collects Failed Incoming Sms related information. */ + public void onIncomingSmsError(@InboundSmsHandler.SmsSource int smsSource, int result) { + String status = "Failure"; + String failureReason = getIncomingSmsErrorString(result); + logIncomingSms(smsSource, status, failureReason); + Rlog.d( + TAG, + " smsSource = " + + smsSource + + "Result = " + + result + + "IncomingError = " + + failureReason + + "(" + + getIncomingError(result) + + ")"); + } + + private void logOutgoingSms(boolean isOverIms, @SmsManager.Result int sendErrorCode) { + try { + String type = "SMS Outgoing"; + String status = sendErrorCode == 0 ? "Success" : "Failure"; + int rat = getRat(isOverIms); + String ratString = TelephonyManager.getNetworkTypeName(rat); + String failureReason = + status.equals("Success") ? "NA" : getSmsFailureReasonString(sendErrorCode); + Rlog.d( + TAG, + "SlotInfo = " + + mSlotIndex + + " Type = " + + type + + " Status = " + + status + + "RAT " + + ratString + + " " + + rat + + "Failure Reason = " + + failureReason); + sendDataToProvider(status, type, ratString, failureReason); + + } catch (Exception e) { + Rlog.d(TAG, "Error in SmsLogs" + e); + } + } + + private void logIncomingSms( + @InboundSmsHandler.SmsSource int smsSource, String status, String failureReason) { + String type = "SMS Incoming"; + try { + int rat = getRat(smsSource); + String ratString = TelephonyManager.getNetworkTypeName(rat); + sendDataToProvider(status, type, ratString, failureReason); + Rlog.d( + TAG, + "SlotInfo =" + + mSlotIndex + + " Type = " + + type + + " Status = " + + status + + " RAT " + + ratString + + " (" + + rat + + " ) Failure Reason = " + + failureReason); + } catch (Exception e) { + Rlog.e(TAG, "Exception = " + e); + } + } + + private void sendDataToProvider( + String status, String type, String rat, String failureReason) { + mExecutorService.execute(() -> { + mSmsMmsAnalyticsProvider.insertDataToDb(status, type, rat, failureReason); + }); + } + + private static int getIncomingError(int result) { + switch (result) { + case Activity.RESULT_OK: + case Intents.RESULT_SMS_HANDLED: + return INCOMING_SMS__ERROR__SMS_SUCCESS; + case Intents.RESULT_SMS_OUT_OF_MEMORY: + return INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY; + case Intents.RESULT_SMS_UNSUPPORTED: + return INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED; + case Intents.RESULT_SMS_GENERIC_ERROR: + default: + return INCOMING_SMS__ERROR__SMS_ERROR_GENERIC; + } + } + + private static String getIncomingSmsErrorString(int result) { + switch (result) { + case Activity.RESULT_OK: + case Intents.RESULT_SMS_HANDLED: + return "INCOMING_SMS__ERROR__SMS_SUCCESS"; + case Intents.RESULT_SMS_OUT_OF_MEMORY: + return "INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY"; + case Intents.RESULT_SMS_UNSUPPORTED: + return "INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED"; + case Intents.RESULT_SMS_GENERIC_ERROR: + default: + return "INCOMING_SMS__ERROR__SMS_ERROR_GENERIC"; + } + } + + @Nullable + private ServiceState getServiceState() { + Phone phone = mPhone; + if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) { + phone = mPhone.getDefaultPhone(); + } + ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker(); + return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null; + } + + @Annotation.NetworkType + private int getRat(@InboundSmsHandler.SmsSource int smsSource) { + if (smsSource == SOURCE_INJECTED_FROM_UNKNOWN) { + return TelephonyManager.NETWORK_TYPE_UNKNOWN; + } + return getRat(smsSource == SOURCE_INJECTED_FROM_IMS); + } + + @Annotation.NetworkType + private int getRat(boolean isOverIms) { + if (isOverIms) { + if (mPhone.getImsRegistrationTech() + == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) { + return TelephonyManager.NETWORK_TYPE_IWLAN; + } + } + ServiceState serviceState = getServiceState(); + return serviceState != null + ? serviceState.getVoiceNetworkType() + : TelephonyManager.NETWORK_TYPE_UNKNOWN; + } + + private String getSmsFailureReasonString(int sendErrorCode) { + switch (sendErrorCode) { + case SmsManager.RESULT_ERROR_NONE: + return "RESULT_ERROR_NONE"; + case SmsManager.RESULT_ERROR_GENERIC_FAILURE: + return "RESULT_ERROR_GENERIC_FAILURE"; + case SmsManager.RESULT_ERROR_RADIO_OFF: + return "RESULT_ERROR_RADIO_OFF"; + case SmsManager.RESULT_ERROR_NULL_PDU: + return "RESULT_ERROR_NULL_PDU"; + case SmsManager.RESULT_ERROR_NO_SERVICE: + return "RESULT_ERROR_NO_SERVICE"; + case SmsManager.RESULT_ERROR_LIMIT_EXCEEDED: + return "RESULT_ERROR_LIMIT_EXCEEDED"; + case SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE: + return "RESULT_ERROR_FDN_CHECK_FAILURE"; + case SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED: + return "RESULT_ERROR_SHORT_CODE_NOT_ALLOWED"; + case SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED: + return "RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED"; + case SmsManager.RESULT_RADIO_NOT_AVAILABLE: + return "RESULT_RADIO_NOT_AVAILABLE"; + case SmsManager.RESULT_NETWORK_REJECT: + return "RESULT_NETWORK_REJECT"; + case SmsManager.RESULT_INVALID_ARGUMENTS: + return "RESULT_INVALID_ARGUMENTS"; + case SmsManager.RESULT_INVALID_STATE: + return "RESULT_INVALID_STATE"; + case SmsManager.RESULT_NO_MEMORY: + return "RESULT_NO_MEMORY"; + case SmsManager.RESULT_INVALID_SMS_FORMAT: + return "RESULT_INVALID_SMS_FORMAT"; + case SmsManager.RESULT_SYSTEM_ERROR: + return "RESULT_SYSTEM_ERROR"; + case SmsManager.RESULT_MODEM_ERROR: + return "RESULT_MODEM_ERROR"; + case SmsManager.RESULT_NETWORK_ERROR: + return "RESULT_NETWORK_ERROR"; + case SmsManager.RESULT_INVALID_SMSC_ADDRESS: + return "RESULT_INVALID_SMSC_ADDRESS"; + case SmsManager.RESULT_OPERATION_NOT_ALLOWED: + return "RESULT_OPERATION_NOT_ALLOWED"; + case SmsManager.RESULT_INTERNAL_ERROR: + return "RESULT_INTERNAL_ERROR"; + case SmsManager.RESULT_NO_RESOURCES: + return "RESULT_NO_RESOURCES"; + case SmsManager.RESULT_CANCELLED: + return "RESULT_CANCELLED"; + case SmsManager.RESULT_REQUEST_NOT_SUPPORTED: + return "RESULT_REQUEST_NOT_SUPPORTED"; + case SmsManager.RESULT_NO_BLUETOOTH_SERVICE: + return "RESULT_NO_BLUETOOTH_SERVICE"; + case SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS: + return "RESULT_INVALID_BLUETOOTH_ADDRESS"; + case SmsManager.RESULT_BLUETOOTH_DISCONNECTED: + return "RESULT_BLUETOOTH_DISCONNECTED"; + case SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING: + return "RESULT_UNEXPECTED_EVENT_STOP_SENDING"; + case SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY: + return "RESULT_SMS_BLOCKED_DURING_EMERGENCY"; + case SmsManager.RESULT_SMS_SEND_RETRY_FAILED: + return "RESULT_SMS_SEND_RETRY_FAILED"; + case SmsManager.RESULT_REMOTE_EXCEPTION: + return "RESULT_REMOTE_EXCEPTION"; + case SmsManager.RESULT_NO_DEFAULT_SMS_APP: + return "RESULT_NO_DEFAULT_SMS_APP"; + case SmsManager.RESULT_USER_NOT_ALLOWED: + return "RESULT_USER_NOT_ALLOWED"; + case SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE: + return "RESULT_RIL_RADIO_NOT_AVAILABLE"; + case SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY: + return "RESULT_RIL_SMS_SEND_FAIL_RETRY"; + case SmsManager.RESULT_RIL_NETWORK_REJECT: + return "RESULT_RIL_NETWORK_REJECT"; + case SmsManager.RESULT_RIL_INVALID_STATE: + return "RESULT_RIL_INVALID_STATE"; + case SmsManager.RESULT_RIL_INVALID_ARGUMENTS: + return "RESULT_RIL_INVALID_ARGUMENTS"; + case SmsManager.RESULT_RIL_NO_MEMORY: + return "RESULT_RIL_NO_MEMORY"; + case SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED: + return "RESULT_RIL_REQUEST_RATE_LIMITED"; + case SmsManager.RESULT_RIL_INVALID_SMS_FORMAT: + return "RESULT_RIL_INVALID_SMS_FORMAT"; + case SmsManager.RESULT_RIL_SYSTEM_ERR: + return "RESULT_RIL_SYSTEM_ERR"; + case SmsManager.RESULT_RIL_ENCODING_ERR: + return "RESULT_RIL_ENCODING_ERR"; + case SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS: + return "RESULT_RIL_INVALID_SMSC_ADDRESS"; + case SmsManager.RESULT_RIL_MODEM_ERR: + return "RESULT_RIL_MODEM_ERR"; + case SmsManager.RESULT_RIL_NETWORK_ERR: + return "RESULT_RIL_NETWORK_ERR"; + case SmsManager.RESULT_RIL_INTERNAL_ERR: + return "RESULT_RIL_INTERNAL_ERR"; + case SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED: + return "RESULT_RIL_REQUEST_NOT_SUPPORTED"; + case SmsManager.RESULT_RIL_INVALID_MODEM_STATE: + return "RESULT_RIL_INVALID_MODEM_STATE"; + case SmsManager.RESULT_RIL_NETWORK_NOT_READY: + return "RESULT_RIL_NETWORK_NOT_READY"; + case SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED: + return "RESULT_RIL_OPERATION_NOT_ALLOWED"; + case SmsManager.RESULT_RIL_NO_RESOURCES: + return "RESULT_RIL_NO_RESOURCES"; + case SmsManager.RESULT_RIL_CANCELLED: + return "RESULT_RIL_CANCELLED"; + case SmsManager.RESULT_RIL_SIM_ABSENT: + return "RESULT_RIL_SIM_ABSENT"; + case SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED: + return "RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED"; + case SmsManager.RESULT_RIL_ACCESS_BARRED: + return "RESULT_RIL_ACCESS_BARRED"; + case SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL: + return "RESULT_RIL_BLOCKED_DUE_TO_CALL"; + case SmsManager.RESULT_RIL_GENERIC_ERROR: + return "RESULT_RIL_GENERIC_ERROR"; + case SmsManager.RESULT_RIL_INVALID_RESPONSE: + return "RESULT_RIL_INVALID_RESPONSE"; + case SmsManager.RESULT_RIL_SIM_PIN2: + return "RESULT_RIL_SIM_PIN2"; + case SmsManager.RESULT_RIL_SIM_PUK2: + return "RESULT_RIL_SIM_PUK2"; + case SmsManager.RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE: + return "RESULT_RIL_SUBSCRIPTION_NOT_AVAILABLE"; + case SmsManager.RESULT_RIL_SIM_ERROR: + return "RESULT_RIL_SIM_ERROR"; + case SmsManager.RESULT_RIL_INVALID_SIM_STATE: + return "RESULT_RIL_INVALID_SIM_STATE"; + case SmsManager.RESULT_RIL_NO_SMS_TO_ACK: + return "RESULT_RIL_NO_SMS_TO_ACK"; + case SmsManager.RESULT_RIL_SIM_BUSY: + return "RESULT_RIL_SIM_BUSY"; + case SmsManager.RESULT_RIL_SIM_FULL: + return "RESULT_RIL_SIM_FULL"; + case SmsManager.RESULT_RIL_NO_SUBSCRIPTION: + return "RESULT_RIL_NO_SUBSCRIPTION"; + case SmsManager.RESULT_RIL_NO_NETWORK_FOUND: + return "RESULT_RIL_NO_NETWORK_FOUND"; + case SmsManager.RESULT_RIL_DEVICE_IN_USE: + return "RESULT_RIL_DEVICE_IN_USE"; + case SmsManager.RESULT_RIL_ABORTED: + return "RESULT_RIL_ABORTED"; + default: + return "NA"; + } + } + } +} |