aboutsummaryrefslogtreecommitdiff
path: root/src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java')
-rw-r--r--src/java/com/android/internal/telephony/analytics/TelephonyAnalytics.java1200
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";
+ }
+ }
+ }
+}