Merge "Remove Recursion from Telecom Session Management/Traversal" into main
diff --git a/proto/pulled_atoms.proto b/proto/pulled_atoms.proto
index 7360b6a..6c9af46 100644
--- a/proto/pulled_atoms.proto
+++ b/proto/pulled_atoms.proto
@@ -101,13 +101,13 @@
  * From frameworks/proto_logging/stats/atoms/telecomm/telecom_extension_atom.proto
  */
 message TelecomErrorStats {
-    // The value should be converted to android.telecom.SubmoduleNameEnum
+    // The value should be converted to android.telecom.SubmoduleEnum
     // From frameworks/proto_logging/stats/enums/telecomm/enums.proto
-    optional int32 submodule_name = 1;
+    optional int32 submodule = 1;
 
-    // The value should be converted to android.telecom.ErrorNameEnum
+    // The value should be converted to android.telecom.ErrorEnum
     // From frameworks/proto_logging/stats/enums/telecomm/enums.proto
-    optional int32 error_name = 2;
+    optional int32 error = 2;
 
     // The number of times this error occurs
     optional int32 count = 3;
diff --git a/src/com/android/server/telecom/CallAudioRouteController.java b/src/com/android/server/telecom/CallAudioRouteController.java
index 5cae393..839b89f 100644
--- a/src/com/android/server/telecom/CallAudioRouteController.java
+++ b/src/com/android/server/telecom/CallAudioRouteController.java
@@ -52,6 +52,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 import com.android.server.telecom.flags.FeatureFlags;
+import com.android.server.telecom.metrics.ErrorStats;
 import com.android.server.telecom.metrics.TelecomMetricsController;
 
 import java.util.ArrayList;
@@ -512,6 +513,10 @@
         if (destRoute == null || (!destRoute.equals(mStreamingRoute)
                 && !getCallSupportedRoutes().contains(destRoute))) {
             Log.i(this, "Ignore routing to unavailable route: %s", destRoute);
+            if (mFeatureFlags.telecomMetricsSupport()) {
+                mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_AUDIO,
+                        ErrorStats.ERROR_AUDIO_ROUTE_UNAVAILABLE);
+            }
             return;
         }
         if (mIsPending) {
@@ -559,6 +564,10 @@
             wiredHeadsetRoute = mAudioRouteFactory.create(AudioRoute.TYPE_WIRED, null,
                     mAudioManager);
         } catch (IllegalArgumentException e) {
+            if (mFeatureFlags.telecomMetricsSupport()) {
+                mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_AUDIO,
+                        ErrorStats.ERROR_EXTERNAL_EXCEPTION);
+            }
             Log.e(this, e, "Can't find available audio device info for route type:"
                     + AudioRoute.DEVICE_TYPE_STRINGS.get(AudioRoute.TYPE_WIRED));
         }
@@ -598,6 +607,10 @@
         try {
             dockRoute = mAudioRouteFactory.create(AudioRoute.TYPE_DOCK, null, mAudioManager);
         } catch (IllegalArgumentException e) {
+            if (mFeatureFlags.telecomMetricsSupport()) {
+                mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_AUDIO,
+                        ErrorStats.ERROR_EXTERNAL_EXCEPTION);
+            }
             Log.e(this, e, "Can't find available audio device info for route type:"
                     + AudioRoute.DEVICE_TYPE_STRINGS.get(AudioRoute.TYPE_WIRED));
         }
@@ -790,6 +803,10 @@
                             mCallsManager.getCurrentUserHandle().getIdentifier(),
                             mContext.getAttributionTag());
                 } catch (RemoteException e) {
+                    if (mFeatureFlags.telecomMetricsSupport()) {
+                        mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_AUDIO,
+                                ErrorStats.ERROR_EXTERNAL_EXCEPTION);
+                    }
                     Log.e(this, e, "Remote exception while toggling mute.");
                     return;
                 }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index e503e91..134bc4f 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -134,6 +134,7 @@
 import com.android.server.telecom.components.ErrorDialogActivity;
 import com.android.server.telecom.components.TelecomBroadcastReceiver;
 import com.android.server.telecom.flags.FeatureFlags;
+import com.android.server.telecom.metrics.ErrorStats;
 import com.android.server.telecom.metrics.TelecomMetricsController;
 import com.android.server.telecom.stats.CallFailureCause;
 import com.android.server.telecom.ui.AudioProcessingNotification;
@@ -530,6 +531,8 @@
     private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
 
     private final MmiUtils mMmiUtils = new MmiUtils();
+
+    private TelecomMetricsController mMetricsController;
     /**
      * Listener to PhoneAccountRegistrar events.
      */
@@ -733,6 +736,7 @@
         mCallStreamingNotification = callStreamingNotification;
         mFeatureFlags = featureFlags;
         mTelephonyFeatureFlags = telephonyFlags;
+        mMetricsController = metricsController;
         mBlockedNumbersManager = mFeatureFlags.telecomMainlineBlockedNumbersManager()
                 ? mContext.getSystemService(BlockedNumbersManager.class)
                 : null;
@@ -2029,10 +2033,18 @@
                     if (exception != null){
                         Log.e(TAG, exception, "Error retrieving list of potential phone accounts.");
                         if (finalCall.isEmergencyCall()) {
+                            if (mFeatureFlags.telecomMetricsSupport()) {
+                                mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_MANAGER,
+                                        ErrorStats.ERROR_RETRIEVING_ACCOUNT_EMERGENCY);
+                            }
                             mAnomalyReporter.reportAnomaly(
                                     EXCEPTION_RETRIEVING_PHONE_ACCOUNTS_EMERGENCY_ERROR_UUID,
                                     EXCEPTION_RETRIEVING_PHONE_ACCOUNTS_EMERGENCY_ERROR_MSG);
                         } else {
+                            if (mFeatureFlags.telecomMetricsSupport()) {
+                                mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_MANAGER,
+                                        ErrorStats.ERROR_RETRIEVING_ACCOUNT);
+                            }
                             mAnomalyReporter.reportAnomaly(
                                     EXCEPTION_RETRIEVING_PHONE_ACCOUNTS_ERROR_UUID,
                                     EXCEPTION_RETRIEVING_PHONE_ACCOUNTS_ERROR_MSG);
@@ -2194,6 +2206,11 @@
                                 showErrorMessage(R.string.cant_call_due_to_no_supported_service);
                                 mListeners.forEach(l -> l.onCreateConnectionFailed(callToPlace));
                                 if (callToPlace.isEmergencyCall()) {
+                                    if (mFeatureFlags.telecomMetricsSupport()) {
+                                        mMetricsController.getErrorStats().log(
+                                                ErrorStats.SUB_CALL_MANAGER,
+                                                ErrorStats.ERROR_EMERGENCY_CALL_ABORTED_NO_ACCOUNT);
+                                    }
                                     mAnomalyReporter.reportAnomaly(
                                             EMERGENCY_CALL_ABORTED_NO_PHONE_ACCOUNTS_ERROR_UUID,
                                             EMERGENCY_CALL_ABORTED_NO_PHONE_ACCOUNTS_ERROR_MSG);
@@ -2219,6 +2236,11 @@
                                         PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)) {
                                     if (SubscriptionManager.getDefaultVoiceSubscriptionId() !=
                                             SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                                        if (mFeatureFlags.telecomMetricsSupport()) {
+                                            mMetricsController.getErrorStats().log(
+                                                    ErrorStats.SUB_CALL_MANAGER,
+                                                    ErrorStats.ERROR_DEFAULT_MO_ACCOUNT_MISMATCH);
+                                        }
                                         mAnomalyReporter.reportAnomaly(
                                                 TELEPHONY_HAS_DEFAULT_BUT_TELECOM_DOES_NOT_UUID,
                                                 TELEPHONY_HAS_DEFAULT_BUT_TELECOM_DOES_NOT_MSG);
@@ -3033,6 +3055,10 @@
                     // If an exceptions is thrown while creating the connection, prompt the user to
                     // generate a bugreport and force disconnect.
                     Log.e(TAG, exception, "Exception thrown while establishing connection.");
+                    if (mFeatureFlags.telecomMetricsSupport()) {
+                        mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_MANAGER,
+                                ErrorStats.ERROR_ESTABLISHING_CONNECTION);
+                    }
                     mAnomalyReporter.reportAnomaly(
                             EXCEPTION_WHILE_ESTABLISHING_CONNECTION_ERROR_UUID,
                             EXCEPTION_WHILE_ESTABLISHING_CONNECTION_ERROR_MSG);
@@ -4160,6 +4186,10 @@
                     }, new LoggedHandlerExecutor(mHandler, "CM.pR", mLock))
                     .exceptionally((throwable) -> {
                         Log.e(TAG, throwable, "Error while executing call removal");
+                        if (mFeatureFlags.telecomMetricsSupport()) {
+                            mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_MANAGER,
+                                    ErrorStats.ERROR_REMOVING_CALL);
+                        }
                         mAnomalyReporter.reportAnomaly(CALL_REMOVAL_EXECUTION_ERROR_UUID,
                                 CALL_REMOVAL_EXECUTION_ERROR_MSG);
                         return null;
@@ -5227,6 +5257,10 @@
 
         // If the live call is stuck in a connecting state, prompt the user to generate a bugreport.
         if (liveCall.getState() == CallState.CONNECTING) {
+            if (mFeatureFlags.telecomMetricsSupport()) {
+                mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_MANAGER,
+                        ErrorStats.ERROR_STUCK_CONNECTING_EMERGENCY);
+            }
             mAnomalyReporter.reportAnomaly(LIVE_CALL_STUCK_CONNECTING_EMERGENCY_ERROR_UUID,
                     LIVE_CALL_STUCK_CONNECTING_EMERGENCY_ERROR_MSG);
         }
@@ -5343,6 +5377,10 @@
         if (liveCall.getState() == CallState.CONNECTING
                 && ((mClockProxy.elapsedRealtime() - liveCall.getCreationElapsedRealtimeMillis())
                 > mTimeoutsAdapter.getNonVoipCallTransitoryStateTimeoutMillis())) {
+            if (mFeatureFlags.telecomMetricsSupport()) {
+                mMetricsController.getErrorStats().log(ErrorStats.SUB_CALL_MANAGER,
+                        ErrorStats.ERROR_STUCK_CONNECTING);
+            }
             mAnomalyReporter.reportAnomaly(LIVE_CALL_STUCK_CONNECTING_ERROR_UUID,
                     LIVE_CALL_STUCK_CONNECTING_ERROR_MSG);
             liveCall.disconnect("Force disconnect CONNECTING call.");
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 529bc79..07ecc11 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -1873,7 +1873,6 @@
         }
     }
 
-    @VisibleForTesting
     public void bringToForeground(boolean showDialpad, UserHandle callingUser) {
         KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class);
         boolean isLockscreenRestricted = keyguardManager != null
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index b8141bf..e6a6dd6 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -56,8 +56,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.provider.BlockedNumberContract;
 import android.provider.BlockedNumbersManager;
@@ -77,16 +75,16 @@
 import android.util.EventLog;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telecom.ICallControl;
 import com.android.internal.telecom.ICallEventCallback;
 import com.android.internal.telecom.ITelecomService;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.modules.utils.BasicShellCommandHandler;
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 import com.android.server.telecom.flags.FeatureFlags;
+import com.android.server.telecom.metrics.ApiStats;
+import com.android.server.telecom.metrics.TelecomMetricsController;
 import com.android.server.telecom.settings.BlockedNumbersActivity;
 import com.android.server.telecom.voip.IncomingCallTransaction;
 import com.android.server.telecom.voip.OutgoingCallTransaction;
@@ -112,45 +110,6 @@
  */
 public class TelecomServiceImpl {
 
-    public interface SubscriptionManagerAdapter {
-        int getDefaultVoiceSubId();
-    }
-
-    static class SubscriptionManagerAdapterImpl implements SubscriptionManagerAdapter {
-        @Override
-        public int getDefaultVoiceSubId() {
-            return SubscriptionManager.getDefaultVoiceSubscriptionId();
-        }
-    }
-
-    public interface SettingsSecureAdapter {
-        void putStringForUser(ContentResolver resolver, String name, String value, int userHandle);
-
-        String getStringForUser(ContentResolver resolver, String name, int userHandle);
-    }
-
-    static class SettingsSecureAdapterImpl implements SettingsSecureAdapter {
-        @Override
-        public void putStringForUser(ContentResolver resolver, String name, String value,
-            int userHandle) {
-            Settings.Secure.putStringForUser(resolver, name, value, userHandle);
-        }
-
-        @Override
-        public String getStringForUser(ContentResolver resolver, String name, int userHandle) {
-            return Settings.Secure.getStringForUser(resolver, name, userHandle);
-        }
-    }
-
-    private static final String TAG = "TelecomServiceImpl";
-    private static final String TIME_LINE_ARG = "timeline";
-    private static final int DEFAULT_VIDEO_STATE = -1;
-    private static final String PERMISSION_HANDLE_CALL_INTENT =
-            "android.permission.HANDLE_CALL_INTENT";
-    private static final String ADD_CALL_ERR_MSG = "Call could not be created or found. "
-            + "Retry operation.";
-    private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
-
     /**
      * Anomaly Report UUIDs and corresponding error descriptions specific to TelecomServiceImpl.
      */
@@ -182,17 +141,38 @@
             UUID.fromString("4edf6c8d-1e43-4c94-b0fc-a40c8d80cfe8");
     public static final String PLACE_CALL_SECURITY_EXCEPTION_ERROR_MSG =
             "Security exception thrown while placing an outgoing call.";
-
-    @VisibleForTesting
-    public void setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter){
-        mAnomalyReporter = mAnomalyReporterAdapter;
-    }
-
+    private static final String TAG = "TelecomServiceImpl";
+    private static final String TIME_LINE_ARG = "timeline";
+    private static final int DEFAULT_VIDEO_STATE = -1;
+    private static final String PERMISSION_HANDLE_CALL_INTENT =
+            "android.permission.HANDLE_CALL_INTENT";
+    private static final String ADD_CALL_ERR_MSG = "Call could not be created or found. "
+            + "Retry operation.";
+    private final PhoneAccountRegistrar mPhoneAccountRegistrar;
+    private final CallIntentProcessor.Adapter mCallIntentProcessorAdapter;
+    private final UserCallIntentProcessorFactory mUserCallIntentProcessorFactory;
+    private final DefaultDialerCache mDefaultDialerCache;
+    private final SubscriptionManagerAdapter mSubscriptionManagerAdapter;
+    private final SettingsSecureAdapter mSettingsSecureAdapter;
+    private final TelecomSystem.SyncRoot mLock;
+    private final TransactionalServiceRepository mTransactionalServiceRepository;
+    private final BlockedNumbersManager mBlockedNumbersManager;
+    private final FeatureFlags mFeatureFlags;
+    private final com.android.internal.telephony.flags.FeatureFlags mTelephonyFeatureFlags;
+    private final TelecomMetricsController mMetricsController;
+    private AnomalyReporterAdapter mAnomalyReporter = new AnomalyReporterAdapterImpl();
+    private final Context mContext;
+    private final AppOpsManager mAppOpsManager;
+    private final PackageManager mPackageManager;
+    private final CallsManager mCallsManager;
+    private TransactionManager mTransactionManager;
     private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
 
         @Override
         public void addCall(CallAttributes callAttributes, ICallEventCallback callEventCallback,
                 String callId, String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ADDCALL,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.aC", Log.getPackageAbbreviation(callingPackage));
                 Log.i(TAG, "addCall: id=[%s], attributes=[%s]", callId, callAttributes);
@@ -205,6 +185,8 @@
                 enforcePhoneAccountIsRegisteredEnabled(handle, handle.getUserHandle());
                 enforceCallingPackage(callingPackage, "addCall");
 
+                event.setResult(ApiStats.RESULT_EXCEPTION);
+
                 // add extras about info used for FGS delegation
                 Bundle extras = new Bundle();
                 extras.putInt(CallAttributes.CALLER_UID_KEY, Binder.getCallingUid());
@@ -268,7 +250,9 @@
                         onAddCallControl(callId, callEventCallback, null, exception);
                     }
                 });
+                event.setResult(ApiStats.RESULT_NORMAL);
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -292,12 +276,17 @@
         @Override
         public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme,
                 String callingPackage, String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_GETDEFAULTOUTGOINGPHONEACCOUNT,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gDOPA", Log.getPackageAbbreviation(callingPackage));
                 synchronized (mLock) {
                     PhoneAccountHandle phoneAccountHandle = null;
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                     long token = Binder.clearCallingIdentity();
+
+                    event.setResult(ApiStats.RESULT_EXCEPTION);
                     try {
                         phoneAccountHandle = mPhoneAccountRegistrar
                                 .getOutgoingPhoneAccountForScheme(uriScheme, callingUserHandle);
@@ -307,6 +296,8 @@
                     } finally {
                         Binder.restoreCallingIdentity(token);
                     }
+
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     if (isCallerSimCallManager(phoneAccountHandle)
                             || canReadPhoneState(
                             callingPackage,
@@ -317,12 +308,16 @@
                     return null;
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
 
         @Override
         public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount(String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_GETUSERSELECTEDOUTGOINGPHONEACCOUNT,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             synchronized (mLock) {
                 try {
                     Log.startSession("TSI.gUSOPA", Log.getPackageAbbreviation(callingPackage));
@@ -330,6 +325,7 @@
                         throw new SecurityException("Only the default dialer, or caller with "
                                 + "READ_PRIVILEGED_PHONE_STATE can call this method.");
                     }
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                     return mPhoneAccountRegistrar.getUserSelectedOutgoingPhoneAccount(
                             callingUserHandle);
@@ -337,6 +333,7 @@
                     Log.e(this, e, "getUserSelectedOutgoingPhoneAccount");
                     throw e;
                 } finally {
+                    logEvent(event);
                     Log.endSession();
                 }
             }
@@ -344,6 +341,9 @@
 
         @Override
         public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_SETUSERSELECTEDOUTGOINGPHONEACCOUNT,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.sUSOPA");
                 synchronized (mLock) {
@@ -353,6 +353,7 @@
                     try {
                         mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(
                                 accountHandle, callingUserHandle);
+                        event.setResult(ApiStats.RESULT_NORMAL);
                     } catch (Exception e) {
                         Log.e(this, e, "setUserSelectedOutgoingPhoneAccount");
                         mAnomalyReporter.reportAnomaly(SET_USER_PHONE_ACCOUNT_ERROR_UUID,
@@ -363,6 +364,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -371,6 +373,9 @@
         public ParceledListSlice<PhoneAccountHandle> getCallCapablePhoneAccounts(
                 boolean includeDisabledAccounts, String callingPackage,
                 String callingFeatureId, boolean acrossProfiles) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_GETCALLCAPABLEPHONEACCOUNTS,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gCCPA", Log.getPackageAbbreviation(callingPackage));
 
@@ -400,13 +405,13 @@
                         "getCallCapablePhoneAccounts")) {
                     return ParceledListSlice.emptyList();
                 }
+                event.setResult(ApiStats.RESULT_NORMAL);
                 synchronized (mLock) {
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
-                    boolean crossUserAccess = mTelephonyFeatureFlags.workProfileApiSplit()
-                            && !acrossProfiles ? false
-                            : (mTelephonyFeatureFlags.workProfileApiSplit()
-                                    ? hasInAppCrossProfilePermission()
-                                    : hasInAppCrossUserPermission());
+                    boolean crossUserAccess = (!mTelephonyFeatureFlags.workProfileApiSplit()
+                            || acrossProfiles) && (mTelephonyFeatureFlags.workProfileApiSplit()
+                            ? hasInAppCrossProfilePermission()
+                            : hasInAppCrossUserPermission());
                     long token = Binder.clearCallingIdentity();
                     try {
                         return new ParceledListSlice<>(
@@ -414,6 +419,7 @@
                                         includeDisabledAccounts, callingUserHandle,
                                         crossUserAccess));
                     } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.e(this, e, "getCallCapablePhoneAccounts");
                         mAnomalyReporter.reportAnomaly(GET_CALL_CAPABLE_ACCOUNTS_ERROR_UUID,
                                 GET_CALL_CAPABLE_ACCOUNTS_ERROR_MSG);
@@ -423,6 +429,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -430,6 +437,9 @@
         @Override
         public ParceledListSlice<PhoneAccountHandle> getSelfManagedPhoneAccounts(
                 String callingPackage, String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_GETSELFMANAGEDPHONEACCOUNTS,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gSMPA", Log.getPackageAbbreviation(callingPackage));
                 if (!canReadPhoneState(callingPackage, callingFeatureId,
@@ -439,10 +449,12 @@
                 synchronized (mLock) {
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return new ParceledListSlice<>(mPhoneAccountRegistrar
                                 .getSelfManagedPhoneAccounts(callingUserHandle));
                     } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.e(this, e, "getSelfManagedPhoneAccounts");
                         throw e;
                     } finally {
@@ -450,6 +462,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -457,6 +470,9 @@
         @Override
         public ParceledListSlice<PhoneAccountHandle> getOwnSelfManagedPhoneAccounts(
                 String callingPackage, String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_GETOWNSELFMANAGEDPHONEACCOUNTS,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gOSMPA", Log.getPackageAbbreviation(callingPackage));
                 try {
@@ -472,11 +488,13 @@
                 synchronized (mLock) {
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return new ParceledListSlice<>(mPhoneAccountRegistrar
                                 .getSelfManagedPhoneAccountsForPackage(callingPackage,
                                         callingUserHandle));
                     } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.e(this, e,
                                 "getSelfManagedPhoneAccountsForPackage");
                         throw e;
@@ -485,6 +503,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -492,6 +511,9 @@
         @Override
         public ParceledListSlice<PhoneAccountHandle> getPhoneAccountsSupportingScheme(
                 String uriScheme, String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_GETPHONEACCOUNTSSUPPORTINGSCHEME,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gPASS", Log.getPackageAbbreviation(callingPackage));
                 try {
@@ -506,11 +528,13 @@
                 synchronized (mLock) {
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return new ParceledListSlice<>(mPhoneAccountRegistrar
-                            .getCallCapablePhoneAccounts(uriScheme, false,
-                                    callingUserHandle, false));
+                                .getCallCapablePhoneAccounts(uriScheme, false,
+                                        callingUserHandle, false));
                     } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.e(this, e, "getPhoneAccountsSupportingScheme %s", uriScheme);
                         throw e;
                     } finally {
@@ -518,6 +542,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -526,42 +551,53 @@
         public ParceledListSlice<PhoneAccountHandle> getPhoneAccountsForPackage(
                 String packageName) {
             //TODO: Deprecate this in S
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETPHONEACCOUNTSFORPACKAGE,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
-                enforceCallingPackage(packageName, "getPhoneAccountsForPackage");
-            } catch (SecurityException se1) {
-                EventLog.writeEvent(0x534e4554, "153995334", Binder.getCallingUid(),
-                        "getPhoneAccountsForPackage: invalid calling package");
-                throw se1;
-            }
-
-            try {
-                enforcePermission(READ_PRIVILEGED_PHONE_STATE);
-            } catch (SecurityException se2) {
-                EventLog.writeEvent(0x534e4554, "153995334", Binder.getCallingUid(),
-                        "getPhoneAccountsForPackage: no permission");
-                throw se2;
-            }
-
-            synchronized (mLock) {
-                final UserHandle callingUserHandle = Binder.getCallingUserHandle();
-                long token = Binder.clearCallingIdentity();
                 try {
-                    Log.startSession("TSI.gPAFP");
-                    return new ParceledListSlice<>(mPhoneAccountRegistrar
-                            .getAllPhoneAccountHandlesForPackage(callingUserHandle, packageName));
-                } catch (Exception e) {
-                    Log.e(this, e, "getPhoneAccountsForPackage %s", packageName);
-                    throw e;
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                    Log.endSession();
+                    enforceCallingPackage(packageName, "getPhoneAccountsForPackage");
+                } catch (SecurityException se1) {
+                    EventLog.writeEvent(0x534e4554, "153995334", Binder.getCallingUid(),
+                            "getPhoneAccountsForPackage: invalid calling package");
+                    throw se1;
                 }
+
+                try {
+                    enforcePermission(READ_PRIVILEGED_PHONE_STATE);
+                } catch (SecurityException se2) {
+                    EventLog.writeEvent(0x534e4554, "153995334", Binder.getCallingUid(),
+                            "getPhoneAccountsForPackage: no permission");
+                    throw se2;
+                }
+
+                synchronized (mLock) {
+                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
+                    try {
+                        Log.startSession("TSI.gPAFP");
+                        return new ParceledListSlice<>(mPhoneAccountRegistrar
+                                .getAllPhoneAccountHandlesForPackage(
+                                        callingUserHandle, packageName));
+                    } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
+                        Log.e(this, e, "getPhoneAccountsForPackage %s", packageName);
+                        throw e;
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                        Log.endSession();
+                    }
+                }
+            } finally {
+                logEvent(event);
             }
         }
 
         @Override
         public PhoneAccount getPhoneAccount(PhoneAccountHandle accountHandle,
                 String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETPHONEACCOUNT,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gPA", Log.getPackageAbbreviation(callingPackage));
                 try {
@@ -588,6 +624,7 @@
                     Set<String> permissions = computePermissionsForBoundPackage(
                             Set.of(MODIFY_PHONE_STATE), null);
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         // In ideal case, we should not resolve the handle across profiles. But
                         // given the fact that profile's call is handled by its parent user's
@@ -598,6 +635,7 @@
                                         /* acrossProfiles */ true);
                         return maybeCleansePhoneAccount(account, permissions);
                     } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.e(this, e, "getPhoneAccount %s", accountHandle);
                         mAnomalyReporter.reportAnomaly(GET_PHONE_ACCOUNT_ERROR_UUID,
                                 GET_PHONE_ACCOUNT_ERROR_MSG);
@@ -607,6 +645,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -614,6 +653,8 @@
         @Override
         public ParceledListSlice<PhoneAccount> getRegisteredPhoneAccounts(String callingPackage,
                 String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETREGISTEREDPHONEACCOUNTS,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gRPA", Log.getPackageAbbreviation(callingPackage));
                 try {
@@ -635,6 +676,7 @@
                 synchronized (mLock) {
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return new ParceledListSlice<>(
                                 mPhoneAccountRegistrar.getPhoneAccounts(
@@ -647,6 +689,7 @@
                                         hasCrossUserAccess /* crossUserAccess */,
                                         false /* includeAll */));
                     } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.e(this, e, "getRegisteredPhoneAccounts");
                         throw e;
                     } finally {
@@ -654,14 +697,18 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
 
         @Override
         public int getAllPhoneAccountsCount() {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETALLPHONEACCOUNTSCOUNT,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gAPAC");
+                event.setCallerUid(Binder.getCallingUid());
                 try {
                     enforceModifyPermission(
                             "getAllPhoneAccountsCount requires MODIFY_PHONE_STATE permission.");
@@ -672,22 +719,27 @@
                 }
 
                 synchronized (mLock) {
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         // This list is pre-filtered for the calling user.
                         return getAllPhoneAccounts().getList().size();
                     } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.e(this, e, "getAllPhoneAccountsCount");
                         throw e;
 
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
 
         @Override
         public ParceledListSlice<PhoneAccount> getAllPhoneAccounts() {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETALLPHONEACCOUNTS,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             synchronized (mLock) {
                 try {
                     Log.startSession("TSI.gAPA");
@@ -702,16 +754,19 @@
 
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return new ParceledListSlice<>(mPhoneAccountRegistrar
                                 .getAllPhoneAccounts(callingUserHandle, false));
                     } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.e(this, e, "getAllPhoneAccounts");
                         throw e;
                     } finally {
                         Binder.restoreCallingIdentity(token);
                     }
                 } finally {
+                    logEvent(event);
                     Log.endSession();
                 }
             }
@@ -719,6 +774,8 @@
 
         @Override
         public ParceledListSlice<PhoneAccountHandle> getAllPhoneAccountHandles() {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETALLPHONEACCOUNTHANDLES,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gAPAH");
 
@@ -735,11 +792,13 @@
                     final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                     boolean crossUserAccess = hasInAppCrossUserPermission();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return new ParceledListSlice<>(mPhoneAccountRegistrar
                                 .getAllPhoneAccountHandles(callingUserHandle,
                                         crossUserAccess));
                     } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.e(this, e, "getAllPhoneAccountsHandles");
                         throw e;
                     } finally {
@@ -747,12 +806,15 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
 
         @Override
         public PhoneAccountHandle getSimCallManager(int subId, String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETSIMCALLMANAGER,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             synchronized (mLock) {
                 try {
                     Log.startSession("TSI.gSCM", Log.getPackageAbbreviation(callingPackage));
@@ -763,6 +825,7 @@
                         if (user != ActivityManager.getCurrentUser()) {
                             enforceCrossUserPermission(callingUid);
                         }
+                        event.setResult(ApiStats.RESULT_NORMAL);
                         return mPhoneAccountRegistrar.getSimCallManager(subId, UserHandle.of(user));
                     } finally {
                         Binder.restoreCallingIdentity(token);
@@ -773,6 +836,7 @@
                             GET_SIM_MANAGER_ERROR_MSG);
                     throw e;
                 } finally {
+                    logEvent(event);
                     Log.endSession();
                 }
             }
@@ -780,6 +844,8 @@
 
         @Override
         public PhoneAccountHandle getSimCallManagerForUser(int user, String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETSIMCALLMANAGERFORUSER,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             synchronized (mLock) {
                 try {
                     Log.startSession("TSI.gSCMFU", Log.getPackageAbbreviation(callingPackage));
@@ -788,6 +854,7 @@
                         enforceCrossUserPermission(callingUid);
                     }
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return mPhoneAccountRegistrar.getSimCallManager(UserHandle.of(user));
                     } finally {
@@ -799,6 +866,7 @@
                             GET_SIM_MANAGER_FOR_USER_ERROR_MSG);
                     throw e;
                 } finally {
+                    logEvent(event);
                     Log.endSession();
                 }
             }
@@ -806,6 +874,8 @@
 
         @Override
         public void registerPhoneAccount(PhoneAccount account, String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_REGISTERPHONEACCOUNT,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.rPA", Log.getPackageAbbreviation(callingPackage));
                 synchronized (mLock) {
@@ -878,6 +948,7 @@
                         }
 
                         final long token = Binder.clearCallingIdentity();
+                        event.setResult(ApiStats.RESULT_NORMAL);
                         try {
                             Log.i(this, "registerPhoneAccount: account=%s",
                                     account);
@@ -893,6 +964,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -900,6 +972,8 @@
         @Override
         public void unregisterPhoneAccount(PhoneAccountHandle accountHandle,
                 String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_UNREGISTERPHONEACCOUNT,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             synchronized (mLock) {
                 try {
                     Log.startSession("TSI.uPA", Log.getPackageAbbreviation(callingPackage));
@@ -907,6 +981,7 @@
                             accountHandle.getComponentName().getPackageName());
                     enforceUserHandleMatchesCaller(accountHandle);
                     final long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         mPhoneAccountRegistrar.unregisterPhoneAccount(accountHandle);
                     } finally {
@@ -916,6 +991,7 @@
                     Log.e(this, e, "unregisterPhoneAccount %s", accountHandle);
                     throw e;
                 } finally {
+                    logEvent(event);
                     Log.endSession();
                 }
             }
@@ -923,16 +999,20 @@
 
         @Override
         public void clearAccounts(String packageName) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_CLEARACCOUNTS,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             synchronized (mLock) {
                 try {
                     Log.startSession("TSI.cA");
                     enforcePhoneAccountModificationForPackage(packageName);
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     mPhoneAccountRegistrar
                             .clearAccounts(packageName, Binder.getCallingUserHandle());
                 } catch (Exception e) {
                     Log.e(this, e, "clearAccounts %s", packageName);
                     throw e;
                 } finally {
+                    logEvent(event);
                     Log.endSession();
                 }
             }
@@ -944,6 +1024,8 @@
         @Override
         public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number,
                 String callingPackage, String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ISVOICEMAILNUMBER,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.iVMN", Log.getPackageAbbreviation(callingPackage));
                 synchronized (mLock) {
@@ -957,9 +1039,11 @@
                         return false;
                     }
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return mPhoneAccountRegistrar.isVoiceMailNumber(accountHandle, number);
                     } catch (Exception e) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.e(this, e, "getSubscriptionIdForPhoneAccount");
                         throw e;
                     } finally {
@@ -967,6 +1051,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -977,6 +1062,8 @@
         @Override
         public String getVoiceMailNumber(PhoneAccountHandle accountHandle, String callingPackage,
                 String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETVOICEMAILNUMBER,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gVMN", Log.getPackageAbbreviation(callingPackage));
                 if (!canReadPhoneState(callingPackage, callingFeatureId, "getVoiceMailNumber")) {
@@ -997,8 +1084,10 @@
                                     .getSubscriptionIdForPhoneAccount(accountHandle);
                         }
                     }
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     return getTelephonyManager(subId).getVoiceMailNumber();
                 } catch (UnsupportedOperationException ignored) {
+                    event.setResult(ApiStats.RESULT_EXCEPTION);
                     Log.w(this, "getVoiceMailNumber: no Telephony");
                     return null;
                 } catch (Exception e) {
@@ -1006,6 +1095,7 @@
                     throw e;
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1016,6 +1106,8 @@
         @Override
         public String getLine1Number(PhoneAccountHandle accountHandle, String callingPackage,
                 String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETLINE1NUMBER,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("getL1N", Log.getPackageAbbreviation(callingPackage));
                 if (!canReadPhoneNumbers(callingPackage, callingFeatureId, "getLine1Number")) {
@@ -1036,8 +1128,10 @@
                         subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
                                 accountHandle);
                     }
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     return getTelephonyManager(subId).getLine1Number();
                 } catch (UnsupportedOperationException ignored) {
+                    event.setResult(ApiStats.RESULT_EXCEPTION);
                     Log.w(this, "getLine1Number: no telephony");
                     return null;
                 } catch (Exception e) {
@@ -1047,6 +1141,7 @@
                     Binder.restoreCallingIdentity(token);
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1056,6 +1151,8 @@
          */
         @Override
         public void silenceRinger(String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_SILENCERINGER,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.sR", Log.getPackageAbbreviation(callingPackage));
                 synchronized (mLock) {
@@ -1063,17 +1160,20 @@
                     UserHandle callingUserHandle = Binder.getCallingUserHandle();
                     boolean crossUserAccess = hasInAppCrossUserPermission();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_EXCEPTION);
                     try {
                         Log.i(this, "Silence Ringer requested by %s", callingPackage);
                         Set<UserHandle> userHandles = mCallsManager.getCallAudioManager().
                                 silenceRingers(mContext, callingUserHandle,
                                         crossUserAccess);
+                        event.setResult(ApiStats.RESULT_NORMAL);
                         mCallsManager.getInCallController().silenceRinger(userHandles);
                     } finally {
                         Binder.restoreCallingIdentity(token);
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1085,10 +1185,13 @@
          */
         @Override
         public ComponentName getDefaultPhoneApp() {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETDEFAULTPHONEAPP,
+                    Binder.getCallingUid(), ApiStats.RESULT_NORMAL);
             try {
                 Log.startSession("TSI.gDPA");
                 return mDefaultDialerCache.getDialtactsSystemDialerComponent();
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1101,6 +1204,8 @@
          */
         @Override
         public String getDefaultDialerPackage(String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETDEFAULTDIALERPACKAGE,
+                    Binder.getCallingUid(), ApiStats.RESULT_NORMAL);
             try {
                 Log.startSession("TSI.gDDP", Log.getPackageAbbreviation(callingPackage));
                 int callerUserId = UserHandle.getCallingUserId();
@@ -1112,6 +1217,7 @@
                     Binder.restoreCallingIdentity(token);
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1125,18 +1231,23 @@
          */
         @Override
         public String getDefaultDialerPackageForUser(int userId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_GETDEFAULTDIALERPACKAGEFORUSER,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gDDPU");
                 mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
                         "READ_PRIVILEGED_PHONE_STATE permission required.");
 
                 final long token = Binder.clearCallingIdentity();
+                event.setResult(ApiStats.RESULT_NORMAL);
                 try {
                     return mDefaultDialerCache.getDefaultDialerApplication(userId);
                 } finally {
                     Binder.restoreCallingIdentity(token);
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1146,10 +1257,13 @@
          */
         @Override
         public String getSystemDialerPackage(String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETSYSTEMDIALERPACKAGE,
+                    Binder.getCallingUid(), ApiStats.RESULT_NORMAL);
             try {
                 Log.startSession("TSI.gSDP", Log.getPackageAbbreviation(callingPackage));
                 return mDefaultDialerCache.getSystemDialerApplication();
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1177,17 +1291,20 @@
          */
         @Override
         public boolean isInCall(String callingPackage, String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ISINCALL,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.iIC", Log.getPackageAbbreviation(callingPackage));
                 if (!canReadPhoneState(callingPackage, callingFeatureId, "isInCall")) {
                     return false;
                 }
-
+                event.setResult(ApiStats.RESULT_NORMAL);
                 synchronized (mLock) {
                     return mCallsManager.hasOngoingCalls(Binder.getCallingUserHandle(),
                             hasInAppCrossUserPermission());
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1197,9 +1314,13 @@
          */
         @Override
         public boolean hasManageOngoingCallsPermission(String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_HASMANAGEONGOINGCALLSPERMISSION,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.hMOCP", Log.getPackageAbbreviation(callingPackage));
                 enforceCallingPackage(callingPackage, "hasManageOngoingCallsPermission");
+                event.setResult(ApiStats.RESULT_NORMAL);
                 return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(
                         mContext, Manifest.permission.MANAGE_ONGOING_CALLS,
                         Binder.getCallingPid(),
@@ -1209,6 +1330,7 @@
                         "Checking whether the caller has MANAGE_ONGOING_CALLS permission")
                         == PermissionChecker.PERMISSION_GRANTED;
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1218,18 +1340,21 @@
          */
         @Override
         public boolean isInManagedCall(String callingPackage, String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ISINMANAGEDCALL,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.iIMC", Log.getPackageAbbreviation(callingPackage));
                 if (!canReadPhoneState(callingPackage, callingFeatureId, "isInManagedCall")) {
                     throw new SecurityException("Only the default dialer or caller with " +
                             "READ_PHONE_STATE permission can use this method.");
                 }
-
+                event.setResult(ApiStats.RESULT_NORMAL);
                 synchronized (mLock) {
                     return mCallsManager.hasOngoingManagedCalls(Binder.getCallingUserHandle(),
                             hasInAppCrossUserPermission());
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1239,6 +1364,8 @@
          */
         @Override
         public boolean isRinging(String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ISRINGING,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.iR");
                 if (!isPrivilegedDialerCalling(callingPackage)) {
@@ -1251,6 +1378,7 @@
                     }
                 }
 
+                event.setResult(ApiStats.RESULT_NORMAL);
                 synchronized (mLock) {
                     // Note: We are explicitly checking the calls telecom is tracking rather than
                     // relying on mCallsManager#getCallState(). Since getCallState() relies on the
@@ -1260,6 +1388,7 @@
                     return mCallsManager.hasRingingOrSimulatedRingingCall();
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1272,6 +1401,8 @@
         @Deprecated
         @Override
         public int getCallState() {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETCALLSTATE,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.getCallState(DEPRECATED)");
                 if (CompatChanges.isChangeEnabled(
@@ -1282,6 +1413,7 @@
                     throw new SecurityException("This method can only be used for applications "
                             + "targeting API version 30 or less.");
                 }
+                event.setResult(ApiStats.RESULT_NORMAL);
                 synchronized (mLock) {
                     return mCallsManager.getCallState();
                 }
@@ -1295,6 +1427,8 @@
          */
         @Override
         public int getCallStateUsingPackage(String callingPackage, String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETCALLSTATEUSINGPACKAGE,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.getCallStateUsingPackage");
 
@@ -1323,10 +1457,12 @@
                                 + " for API version 31+");
                     }
                 }
+                event.setResult(ApiStats.RESULT_NORMAL);
                 synchronized (mLock) {
                     return mCallsManager.getCallState();
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1349,6 +1485,8 @@
          */
         @Override
         public boolean endCall(String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ENDCALL,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.eC", Log.getPackageAbbreviation(callingPackage));
                 synchronized (mLock) {
@@ -1357,6 +1495,7 @@
                     }
 
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return endCallInternal(callingPackage);
                     } finally {
@@ -1364,6 +1503,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1373,12 +1513,15 @@
          */
         @Override
         public void acceptRingingCall(String packageName) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ACCEPTRINGINGCALL,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.aRC", Log.getPackageAbbreviation(packageName));
                 synchronized (mLock) {
                     if (!enforceAnswerCallPermission(packageName, Binder.getCallingUid())) return;
 
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         acceptRingingCallInternal(DEFAULT_VIDEO_STATE, packageName);
                     } finally {
@@ -1386,6 +1529,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1395,12 +1539,16 @@
          */
         @Override
         public void acceptRingingCallWithVideoState(String packageName, int videoState) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_ACCEPTRINGINGCALLWITHVIDEOSTATE,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.aRCWVS", Log.getPackageAbbreviation(packageName));
                 synchronized (mLock) {
                     if (!enforceAnswerCallPermission(packageName, Binder.getCallingUid())) return;
 
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         acceptRingingCallInternal(videoState, packageName);
                     } finally {
@@ -1408,6 +1556,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1418,6 +1567,8 @@
         @Override
         public void showInCallScreen(boolean showDialpad, String callingPackage,
                 String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_SHOWINCALLSCREEN,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.sICS", Log.getPackageAbbreviation(callingPackage));
                 if (!canReadPhoneState(callingPackage, callingFeatureId, "showInCallScreen")) {
@@ -1425,16 +1576,18 @@
                 }
 
                 synchronized (mLock) {
-
                     UserHandle callingUser = Binder.getCallingUserHandle();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
-                        mCallsManager.getInCallController().bringToForeground(showDialpad, callingUser);
+                        mCallsManager.getInCallController().bringToForeground(
+                                showDialpad, callingUser);
                     } finally {
                         Binder.restoreCallingIdentity(token);
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1444,12 +1597,16 @@
          */
         @Override
         public void cancelMissedCallsNotification(String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_CANCELMISSEDCALLSNOTIFICATION,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.cMCN", Log.getPackageAbbreviation(callingPackage));
                 synchronized (mLock) {
                     enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
                     UserHandle userHandle = Binder.getCallingUserHandle();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         mCallsManager.getMissedCallNotifier().clearMissedCalls(userHandle);
                     } finally {
@@ -1457,6 +1614,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1466,6 +1624,8 @@
          */
         @Override
         public boolean handlePinMmi(String dialString, String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_HANDLEPINMMI,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.hPM", Log.getPackageAbbreviation(callingPackage));
                 enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
@@ -1473,6 +1633,7 @@
                 // Switch identity so that TelephonyManager checks Telecom's permissions
                 // instead.
                 long token = Binder.clearCallingIdentity();
+                event.setResult(ApiStats.RESULT_NORMAL);
                 boolean retval = false;
                 try {
                     retval = getTelephonyManager(
@@ -1484,6 +1645,7 @@
 
                 return retval;
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1494,9 +1656,11 @@
         @Override
         public boolean handlePinMmiForPhoneAccount(PhoneAccountHandle accountHandle,
                 String dialString, String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_HANDLEPINMMIFORPHONEACCOUNT,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.hPMFPA", Log.getPackageAbbreviation(callingPackage));
-
                 enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
                 UserHandle callingUserHandle = Binder.getCallingUserHandle();
                 synchronized (mLock) {
@@ -1511,6 +1675,7 @@
                 // Switch identity so that TelephonyManager checks Telecom's permissions
                 // instead.
                 long token = Binder.clearCallingIdentity();
+                event.setResult(ApiStats.RESULT_NORMAL);
                 boolean retval = false;
                 int subId;
                 try {
@@ -1522,6 +1687,7 @@
                         retval = getTelephonyManager(subId)
                                 .handlePinMmiForSubscriber(subId, dialString);
                     } catch (UnsupportedOperationException uoe) {
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.w(this, "handlePinMmiForPhoneAccount: no telephony");
                         retval = false;
                     }
@@ -1530,6 +1696,7 @@
                 }
                 return retval;
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1540,6 +1707,8 @@
         @Override
         public Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle,
                 String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETADNURIFORPHONEACCOUNT,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.aAUFPA", Log.getPackageAbbreviation(callingPackage));
                 enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
@@ -1554,6 +1723,7 @@
                 // Switch identity so that TelephonyManager checks Telecom's permissions
                 // instead.
                 long token = Binder.clearCallingIdentity();
+                event.setResult(ApiStats.RESULT_NORMAL);
                 String retval = "content://icc/adn/";
                 try {
                     long subId = mPhoneAccountRegistrar
@@ -1565,6 +1735,7 @@
 
                 return Uri.parse(retval);
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1574,6 +1745,8 @@
          */
         @Override
         public boolean isTtySupported(String callingPackage, String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ISTTYSUPPORTED,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.iTS", Log.getPackageAbbreviation(callingPackage));
                 if (!canReadPhoneState(callingPackage, callingFeatureId, "isTtySupported")) {
@@ -1581,10 +1754,12 @@
                             "READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE can call this api");
                 }
 
+                event.setResult(ApiStats.RESULT_NORMAL);
                 synchronized (mLock) {
                     return mCallsManager.isTtySupported();
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1594,16 +1769,20 @@
          */
         @Override
         public int getCurrentTtyMode(String callingPackage, String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_GETCURRENTTTYMODE,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.gCTM", Log.getPackageAbbreviation(callingPackage));
                 if (!canReadPhoneState(callingPackage, callingFeatureId, "getCurrentTtyMode")) {
                     return TelecomManager.TTY_MODE_OFF;
                 }
 
+                event.setResult(ApiStats.RESULT_NORMAL);
                 synchronized (mLock) {
                     return mCallsManager.getCurrentTtyMode();
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1614,6 +1793,8 @@
         @Override
         public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras,
                 String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ADDNEWINCOMINGCALL,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.aNIC", Log.getPackageAbbreviation(callingPackage));
                 synchronized (mLock) {
@@ -1647,6 +1828,7 @@
                             }
                         }
                         long token = Binder.clearCallingIdentity();
+                        event.setResult(ApiStats.RESULT_NORMAL);
                         try {
                             Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
                             intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
@@ -1685,11 +1867,14 @@
                             Binder.restoreCallingIdentity(token);
                         }
                     } else {
+                        // Invalid parameters are considered as an exception
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.w(this, "Null phoneAccountHandle. Ignoring request to add new" +
                                 " incoming call");
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1700,6 +1885,8 @@
         @Override
         public void addNewIncomingConference(PhoneAccountHandle phoneAccountHandle, Bundle extras,
                 String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ADDNEWINCOMINGCONFERENCE,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.aNIC", Log.getPackageAbbreviation(callingPackage));
                 synchronized (mLock) {
@@ -1727,6 +1914,7 @@
                             }
                         }
                         long token = Binder.clearCallingIdentity();
+                        event.setResult(ApiStats.RESULT_NORMAL);
                         try {
                             mCallsManager.processIncomingConference(
                                     phoneAccountHandle, extras);
@@ -1734,22 +1922,26 @@
                             Binder.restoreCallingIdentity(token);
                         }
                     } else {
+                        // Invalid parameters are considered as an exception
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.w(this, "Null phoneAccountHandle. Ignoring request to add new" +
                                 " incoming conference");
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
 
-
         /**
          * @see android.telecom.TelecomManager#acceptHandover
          */
         @Override
         public void acceptHandover(Uri srcAddr, int videoState, PhoneAccountHandle destAcct,
                 String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ACCEPTHANDOVER,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.aHO", Log.getPackageAbbreviation(callingPackage));
                 synchronized (mLock) {
@@ -1779,17 +1971,22 @@
                         }
 
                         long token = Binder.clearCallingIdentity();
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         try {
                             mCallsManager.acceptHandover(srcAddr, videoState, destAcct);
+                            event.setResult(ApiStats.RESULT_NORMAL);
                         } finally {
                             Binder.restoreCallingIdentity(token);
                         }
                     } else {
+                        // Invalid parameters are considered as an exception
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.w(this, "Null phoneAccountHandle. Ignoring request " +
                                 "to handover the call");
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1799,6 +1996,8 @@
          */
         @Override
         public void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ADDNEWUNKNOWNCALL,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.aNUC");
                 try {
@@ -1822,7 +2021,7 @@
                         enforcePhoneAccountIsRegisteredEnabled(phoneAccountHandle,
                                 Binder.getCallingUserHandle());
                         long token = Binder.clearCallingIdentity();
-
+                        event.setResult(ApiStats.RESULT_NORMAL);
                         try {
                             Intent intent = new Intent(TelecomManager.ACTION_NEW_UNKNOWN_CALL);
                             if (extras != null) {
@@ -1838,12 +2037,15 @@
                             Binder.restoreCallingIdentity(token);
                         }
                     } else {
+                        // Invalid parameters are considered as an exception
+                        event.setResult(ApiStats.RESULT_EXCEPTION);
                         Log.i(this,
                                 "Null phoneAccountHandle or not initiated by Telephony. " +
                                         "Ignoring request to add new unknown call.");
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1854,6 +2056,8 @@
         @Override
         public void startConference(List<Uri> participants, Bundle extras,
                 String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_STARTCONFERENCE,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.sC", Log.getPackageAbbreviation(callingPackage));
                 if (!canCallPhone(callingPackage, "startConference")) {
@@ -1863,6 +2067,7 @@
                 // Binder is clearing the identity, so we need to keep the store the handle
                 UserHandle currentUserHandle = Binder.getCallingUserHandle();
                 long token = Binder.clearCallingIdentity();
+                event.setResult(ApiStats.RESULT_NORMAL);
                 try {
                     mCallsManager.startConference(participants, extras, callingPackage,
                             currentUserHandle);
@@ -1870,6 +2075,7 @@
                     Binder.restoreCallingIdentity(token);
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1880,6 +2086,8 @@
         @Override
         public void placeCall(Uri handle, Bundle extras, String callingPackage,
                 String callingFeatureId) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_PLACECALL,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.pC", Log.getPackageAbbreviation(callingPackage));
                 enforceCallingPackage(callingPackage, "placeCall");
@@ -1955,6 +2163,7 @@
                 synchronized (mLock) {
                     final UserHandle userHandle = Binder.getCallingUserHandle();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         final Intent intent = new Intent(hasCallPrivilegedPermission ?
                                 Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
@@ -1972,6 +2181,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -1981,11 +2191,14 @@
          */
         @Override
         public boolean enablePhoneAccount(PhoneAccountHandle accountHandle, boolean isEnabled) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ENABLEPHONEACCOUNT,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.ePA");
                 enforceModifyPermission();
                 synchronized (mLock) {
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         // enable/disable phone account
                         return mPhoneAccountRegistrar.enablePhoneAccount(accountHandle, isEnabled);
@@ -1994,12 +2207,15 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
 
         @Override
         public boolean setDefaultDialer(String packageName) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_SETDEFAULTDIALER,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.sDD");
                 enforcePermission(MODIFY_PHONE_STATE);
@@ -2007,6 +2223,7 @@
                 synchronized (mLock) {
                     int callerUserId = UserHandle.getCallingUserId();
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return mDefaultDialerCache.setDefaultDialer(packageName,
                                 callerUserId);
@@ -2015,6 +2232,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -2047,11 +2265,15 @@
 
         @Override
         public TelecomAnalytics dumpCallAnalytics() {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_DUMPCALLANALYTICS,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.dCA");
                 enforcePermission(DUMP);
+                event.setResult(ApiStats.RESULT_NORMAL);
                 return Analytics.dumpToParcelableAnalytics();
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -2066,6 +2288,8 @@
          */
         @Override
         protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_DUMP,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             if (mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -2075,6 +2299,8 @@
                 return;
             }
 
+            event.setResult(ApiStats.RESULT_NORMAL);
+            logEvent(event);
 
             if (args != null && args.length > 0 && Analytics.ANALYTICS_DUMPSYS_ARG.equals(
                     args[0])) {
@@ -2154,7 +2380,7 @@
                 }
 
                 for (Method m : methods) {
-                    String flagEnabled = (Boolean) m.invoke(mFeatureFlags) ? "[✅]": "[❌]";
+                    String flagEnabled = (Boolean) m.invoke(mFeatureFlags) ? "[✅]" : "[❌]";
                     String methodName = m.getName();
                     String camelCaseName = methodName.replaceAll("([a-z])([A-Z]+)", "$1_$2")
                             .toLowerCase(Locale.US);
@@ -2171,17 +2397,23 @@
          */
         @Override
         public Intent createManageBlockedNumbersIntent(String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_CREATEMANAGEBLOCKEDNUMBERSINTENT,
+                    Binder.getCallingUid(), ApiStats.RESULT_NORMAL);
             try {
                 Log.startSession("TSI.cMBNI", Log.getPackageAbbreviation(callingPackage));
                 return BlockedNumbersActivity.getIntentForStartingActivity();
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
 
-
         @Override
         public Intent createLaunchEmergencyDialerIntent(String number) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(
+                    ApiStats.API_CREATELAUNCHEMERGENCYDIALERINTENT,
+                    Binder.getCallingUid(), ApiStats.RESULT_NORMAL);
             String packageName = mContext.getApplicationContext().getString(
                     com.android.internal.R.string.config_emergency_dialer_package);
             Intent intent = new Intent(Intent.ACTION_DIAL_EMERGENCY)
@@ -2194,6 +2426,7 @@
             if (!TextUtils.isEmpty(number) && TextUtils.isDigitsOnly(number)) {
                 intent.setData(Uri.parse("tel:" + number));
             }
+            logEvent(event);
             return intent;
         }
 
@@ -2203,6 +2436,8 @@
         @Override
         public boolean isIncomingCallPermitted(PhoneAccountHandle phoneAccountHandle,
                 String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ISINCOMINGCALLPERMITTED,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             Log.startSession("TSI.iICP", Log.getPackageAbbreviation(callingPackage));
             try {
                 enforceCallingPackage(callingPackage, "isIncomingCallPermitted");
@@ -2211,6 +2446,7 @@
                 enforceUserHandleMatchesCaller(phoneAccountHandle);
                 synchronized (mLock) {
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return mCallsManager.isIncomingCallPermitted(phoneAccountHandle);
                     } finally {
@@ -2218,6 +2454,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -2228,6 +2465,8 @@
         @Override
         public boolean isOutgoingCallPermitted(PhoneAccountHandle phoneAccountHandle,
                 String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ISOUTGOINGCALLPERMITTED,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             Log.startSession("TSI.iOCP", Log.getPackageAbbreviation(callingPackage));
             try {
                 enforceCallingPackage(callingPackage, "isOutgoingCallPermitted");
@@ -2236,6 +2475,7 @@
                 enforceUserHandleMatchesCaller(phoneAccountHandle);
                 synchronized (mLock) {
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return mCallsManager.isOutgoingCallPermitted(phoneAccountHandle);
                     } finally {
@@ -2243,6 +2483,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -2296,11 +2537,14 @@
          */
         @Override
         public boolean isInEmergencyCall() {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ISINEMERGENCYCALL,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 Log.startSession("TSI.iIEC");
                 enforceModifyPermission();
                 synchronized (mLock) {
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         boolean isInEmergencyCall = mCallsManager.isInEmergencyCall();
                         Log.i(this, "isInEmergencyCall: %b", isInEmergencyCall);
@@ -2310,6 +2554,7 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
@@ -2383,7 +2628,7 @@
             }
         }
 
-        private boolean isDisconnectingOrDisconnected(Call call){
+        private boolean isDisconnectingOrDisconnected(Call call) {
             return call.getState() == CallState.DISCONNECTED
                     || call.getState() == CallState.DISCONNECTING;
         }
@@ -2631,6 +2876,8 @@
         @Override
         public boolean isInSelfManagedCall(String packageName, UserHandle userHandle,
                 String callingPackage) {
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(ApiStats.API_ISINSELFMANAGEDCALL,
+                    Binder.getCallingUid(), ApiStats.RESULT_PERMISSION);
             try {
                 mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
                         "READ_PRIVILEGED_PHONE_STATE required.");
@@ -2643,6 +2890,7 @@
                 Log.startSession("TSI.iISMC", Log.getPackageAbbreviation(callingPackage));
                 synchronized (mLock) {
                     long token = Binder.clearCallingIdentity();
+                    event.setResult(ApiStats.RESULT_NORMAL);
                     try {
                         return mCallsManager.isInSelfManagedCall(
                                 packageName, userHandle);
@@ -2651,10 +2899,68 @@
                     }
                 }
             } finally {
+                logEvent(event);
                 Log.endSession();
             }
         }
     };
+    public TelecomServiceImpl(
+            Context context,
+            CallsManager callsManager,
+            PhoneAccountRegistrar phoneAccountRegistrar,
+            CallIntentProcessor.Adapter callIntentProcessorAdapter,
+            UserCallIntentProcessorFactory userCallIntentProcessorFactory,
+            DefaultDialerCache defaultDialerCache,
+            SubscriptionManagerAdapter subscriptionManagerAdapter,
+            SettingsSecureAdapter settingsSecureAdapter,
+            FeatureFlags featureFlags,
+            com.android.internal.telephony.flags.FeatureFlags telephonyFeatureFlags,
+            TelecomSystem.SyncRoot lock, TelecomMetricsController metricsController) {
+        mContext = context;
+        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+
+        mPackageManager = mContext.getPackageManager();
+
+        mCallsManager = callsManager;
+        mFeatureFlags = featureFlags;
+        if (telephonyFeatureFlags != null) {
+            mTelephonyFeatureFlags = telephonyFeatureFlags;
+        } else {
+            mTelephonyFeatureFlags =
+                    new com.android.internal.telephony.flags.FeatureFlagsImpl();
+        }
+        mLock = lock;
+        mPhoneAccountRegistrar = phoneAccountRegistrar;
+        mUserCallIntentProcessorFactory = userCallIntentProcessorFactory;
+        mDefaultDialerCache = defaultDialerCache;
+        mCallIntentProcessorAdapter = callIntentProcessorAdapter;
+        mSubscriptionManagerAdapter = subscriptionManagerAdapter;
+        mSettingsSecureAdapter = settingsSecureAdapter;
+        mMetricsController = metricsController;
+
+        mDefaultDialerCache.observeDefaultDialerApplication(mContext.getMainExecutor(), userId -> {
+            String defaultDialer = mDefaultDialerCache.getDefaultDialerApplication(userId);
+            if (defaultDialer == null) {
+                // We are replacing the dialer, just wait for the upcoming callback.
+                return;
+            }
+            final Intent intent = new Intent(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED)
+                    .putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
+                            defaultDialer);
+            mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+        });
+
+        mTransactionManager = TransactionManager.getInstance();
+        mTransactionalServiceRepository = new TransactionalServiceRepository();
+        mBlockedNumbersManager = mFeatureFlags.telecomMainlineBlockedNumbersManager()
+                ? mContext.getSystemService(BlockedNumbersManager.class)
+                : null;
+    }
+
+    @VisibleForTesting
+    public void setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter) {
+        mAnomalyReporter = mAnomalyReporterAdapter;
+    }
 
     private boolean enforceCallStreamingPermission(String packageName, PhoneAccountHandle handle,
             int uid) {
@@ -2684,7 +2990,7 @@
             final int opCode = AppOpsManager.permissionToOpCode(permission);
             if (opCode != AppOpsManager.OP_NONE
                     && mAppOpsManager.checkOp(opCode, uid, packageName)
-                        != AppOpsManager.MODE_ALLOWED) {
+                    != AppOpsManager.MODE_ALLOWED) {
                 return false;
             }
         }
@@ -2701,85 +3007,12 @@
                 "App requires ACCEPT_HANDOVER permission to accept handovers.");
 
         final int opCode = AppOpsManager.permissionToOpCode(Manifest.permission.ACCEPT_HANDOVER);
-        if (opCode != AppOpsManager.OP_ACCEPT_HANDOVER || (
-                mAppOpsManager.checkOp(opCode, uid, packageName)
-                        != AppOpsManager.MODE_ALLOWED)) {
-            return false;
-        }
-        return true;
-    }
-
-    private Context mContext;
-    private AppOpsManager mAppOpsManager;
-    private PackageManager mPackageManager;
-    private CallsManager mCallsManager;
-    private final PhoneAccountRegistrar mPhoneAccountRegistrar;
-    private final CallIntentProcessor.Adapter mCallIntentProcessorAdapter;
-    private final UserCallIntentProcessorFactory mUserCallIntentProcessorFactory;
-    private final DefaultDialerCache mDefaultDialerCache;
-    private final SubscriptionManagerAdapter mSubscriptionManagerAdapter;
-    private final SettingsSecureAdapter mSettingsSecureAdapter;
-    private final TelecomSystem.SyncRoot mLock;
-    private TransactionManager mTransactionManager;
-    private final TransactionalServiceRepository mTransactionalServiceRepository;
-    private final BlockedNumbersManager mBlockedNumbersManager;
-    private final FeatureFlags mFeatureFlags;
-    private final com.android.internal.telephony.flags.FeatureFlags mTelephonyFeatureFlags;
-
-    public TelecomServiceImpl(
-            Context context,
-            CallsManager callsManager,
-            PhoneAccountRegistrar phoneAccountRegistrar,
-            CallIntentProcessor.Adapter callIntentProcessorAdapter,
-            UserCallIntentProcessorFactory userCallIntentProcessorFactory,
-            DefaultDialerCache defaultDialerCache,
-            SubscriptionManagerAdapter subscriptionManagerAdapter,
-            SettingsSecureAdapter settingsSecureAdapter,
-            FeatureFlags featureFlags,
-            com.android.internal.telephony.flags.FeatureFlags telephonyFeatureFlags,
-            TelecomSystem.SyncRoot lock) {
-        mContext = context;
-        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
-
-        mPackageManager = mContext.getPackageManager();
-
-        mCallsManager = callsManager;
-        mFeatureFlags = featureFlags;
-        if (telephonyFeatureFlags != null) {
-            mTelephonyFeatureFlags = telephonyFeatureFlags;
-        } else {
-            mTelephonyFeatureFlags =
-                    new com.android.internal.telephony.flags.FeatureFlagsImpl();
-        }
-        mLock = lock;
-        mPhoneAccountRegistrar = phoneAccountRegistrar;
-        mUserCallIntentProcessorFactory = userCallIntentProcessorFactory;
-        mDefaultDialerCache = defaultDialerCache;
-        mCallIntentProcessorAdapter = callIntentProcessorAdapter;
-        mSubscriptionManagerAdapter = subscriptionManagerAdapter;
-        mSettingsSecureAdapter = settingsSecureAdapter;
-
-        mDefaultDialerCache.observeDefaultDialerApplication(mContext.getMainExecutor(), userId -> {
-            String defaultDialer = mDefaultDialerCache.getDefaultDialerApplication(userId);
-            if (defaultDialer == null) {
-                // We are replacing the dialer, just wait for the upcoming callback.
-                return;
-            }
-            final Intent intent = new Intent(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED)
-                    .putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
-                            defaultDialer);
-            mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
-        });
-
-        mTransactionManager = TransactionManager.getInstance();
-        mTransactionalServiceRepository = new TransactionalServiceRepository();
-        mBlockedNumbersManager = mFeatureFlags.telecomMainlineBlockedNumbersManager()
-                ? mContext.getSystemService(BlockedNumbersManager.class)
-                : null;
+        return opCode == AppOpsManager.OP_ACCEPT_HANDOVER
+                && (mAppOpsManager.checkOp(opCode, uid, packageName) == AppOpsManager.MODE_ALLOWED);
     }
 
     @VisibleForTesting
-    public void setTransactionManager(TransactionManager transactionManager){
+    public void setTransactionManager(TransactionManager transactionManager) {
         mTransactionManager = transactionManager;
     }
 
@@ -2787,10 +3020,6 @@
         return mBinderImpl;
     }
 
-    //
-    // Supporting methods for the ITelecomService interface implementation.
-    //
-
     private boolean isPhoneAccountHandleVisibleToCallingUser(
             PhoneAccountHandle phoneAccountUserHandle, UserHandle callingUser) {
         synchronized (mLock) {
@@ -2839,6 +3068,10 @@
         }
     }
 
+    //
+    // Supporting methods for the ITelecomService interface implementation.
+    //
+
     private boolean endCallInternal(String callingPackage) {
         // Always operate on the foreground call if one exists, otherwise get the first call in
         // priority order by call-state.
@@ -2880,14 +3113,14 @@
     // Enforce that the PhoneAccountHandle being passed in is both registered to the current user
     // and enabled.
     private void enforcePhoneAccountIsRegisteredEnabled(PhoneAccountHandle phoneAccountHandle,
-                                                        UserHandle callingUserHandle) {
+            UserHandle callingUserHandle) {
         PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle,
                 callingUserHandle);
-        if(phoneAccount == null) {
+        if (phoneAccount == null) {
             EventLog.writeEvent(0x534e4554, "26864502", Binder.getCallingUid(), "R");
             throw new SecurityException("This PhoneAccountHandle is not registered for this user!");
         }
-        if(!phoneAccount.isEnabled()) {
+        if (!phoneAccount.isEnabled()) {
             EventLog.writeEvent(0x534e4554, "26864502", Binder.getCallingUid(), "E");
             throw new SecurityException("This PhoneAccountHandle is not enabled for this user!");
         }
@@ -2959,7 +3192,7 @@
     /**
      * helper method that compares the binder_uid to what the packageManager_uid reports for the
      * passed in packageName.
-     *
+     * <p>
      * returns true if the binder_uid matches the packageManager_uid records
      */
     private boolean callingUidMatchesPackageManagerRecords(String packageName) {
@@ -2967,13 +3200,12 @@
         int callingUid = Binder.getCallingUid();
         PackageManager pm;
         long token = Binder.clearCallingIdentity();
-        try{
+        try {
             pm = mContext.createContextAsUser(
                     UserHandle.getUserHandleForUid(callingUid), 0).getPackageManager();
-        }
-        catch (Exception e){
+        } catch (Exception e) {
             Log.i(this, "callingUidMatchesPackageManagerRecords:"
-                            + " createContextAsUser hit exception=[%s]", e.toString());
+                    + " createContextAsUser hit exception=[%s]", e.toString());
             return false;
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -2988,7 +3220,7 @@
 
         if (packageUid != callingUid) {
             Log.i(this, "callingUidMatchesPackageManagerRecords: uid mismatch found for"
-                            + "packageName=[%s]. packageManager reports packageUid=[%d] but "
+                    + "packageName=[%s]. packageManager reports packageUid=[%d] but "
                     + "binder reports callingUid=[%d]", packageName, packageUid, callingUid);
         }
 
@@ -3079,7 +3311,7 @@
         boolean permissionsOk =
                 isCallerSimCallManagerForAnySim(account.getAccountHandle())
                         || mContext.checkCallingOrSelfPermission(REGISTER_SIM_SUBSCRIPTION)
-                                == PackageManager.PERMISSION_GRANTED;
+                        == PackageManager.PERMISSION_GRANTED;
         if (!prerequisiteCapabilitiesOk || !permissionsOk) {
             throw new SecurityException(
                     "Only SIM subscriptions and connection managers are allowed to declare "
@@ -3091,7 +3323,7 @@
     private void enforceRegisterSkipCallFiltering() {
         if (!isCallerSystemApp()) {
             throw new SecurityException(
-                "EXTRA_SKIP_CALL_FILTERING is only available to system apps.");
+                    "EXTRA_SKIP_CALL_FILTERING is only available to system apps.");
         }
     }
 
@@ -3261,9 +3493,9 @@
 
     private boolean isSelfManagedConnectionService(PhoneAccountHandle phoneAccountHandle) {
         if (phoneAccountHandle != null) {
-                PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
-                        phoneAccountHandle);
-                return phoneAccount != null && phoneAccount.isSelfManaged();
+            PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
+                    phoneAccountHandle);
+            return phoneAccount != null && phoneAccount.isSelfManaged();
         }
         return false;
     }
@@ -3399,7 +3631,7 @@
     }
 
     private void broadcastCallScreeningAppChangedIntent(String componentName,
-        boolean isDefault) {
+            boolean isDefault) {
         if (TextUtils.isEmpty(componentName)) {
             return;
         }
@@ -3408,11 +3640,11 @@
 
         if (broadcastComponentName != null) {
             Intent intent = new Intent(TelecomManager
-                .ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED);
+                    .ACTION_DEFAULT_CALL_SCREENING_APP_CHANGED);
             intent.putExtra(TelecomManager
-                .EXTRA_IS_DEFAULT_CALL_SCREENING_APP, isDefault);
+                    .EXTRA_IS_DEFAULT_CALL_SCREENING_APP, isDefault);
             intent.putExtra(TelecomManager
-                .EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME, componentName);
+                    .EXTRA_DEFAULT_CALL_SCREENING_APP_COMPONENT_NAME, componentName);
             intent.setPackage(broadcastComponentName.getPackageName());
             mContext.sendBroadcast(intent);
         }
@@ -3451,4 +3683,40 @@
             }
         }
     }
+
+    private void logEvent(ApiStats.ApiEvent event) {
+        if (mFeatureFlags.telecomMetricsSupport()) {
+            mMetricsController.getApiStats().log(event);
+        }
+    }
+
+    public interface SubscriptionManagerAdapter {
+        int getDefaultVoiceSubId();
+    }
+
+    public interface SettingsSecureAdapter {
+        void putStringForUser(ContentResolver resolver, String name, String value, int userHandle);
+
+        String getStringForUser(ContentResolver resolver, String name, int userHandle);
+    }
+
+    static class SubscriptionManagerAdapterImpl implements SubscriptionManagerAdapter {
+        @Override
+        public int getDefaultVoiceSubId() {
+            return SubscriptionManager.getDefaultVoiceSubscriptionId();
+        }
+    }
+
+    static class SettingsSecureAdapterImpl implements SettingsSecureAdapter {
+        @Override
+        public void putStringForUser(ContentResolver resolver, String name, String value,
+                int userHandle) {
+            Settings.Secure.putStringForUser(resolver, name, value, userHandle);
+        }
+
+        @Override
+        public String getStringForUser(ContentResolver resolver, String name, int userHandle) {
+            return Settings.Secure.getStringForUser(resolver, name, userHandle);
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index fd1053f..34afdb0 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -502,7 +502,8 @@
                     new TelecomServiceImpl.SettingsSecureAdapterImpl(),
                     featureFlags,
                     null,
-                    mLock);
+                    mLock,
+                    metricsController);
         } finally {
             Log.endSession();
         }
diff --git a/src/com/android/server/telecom/metrics/ApiStats.java b/src/com/android/server/telecom/metrics/ApiStats.java
index b37569f..da9e04f 100644
--- a/src/com/android/server/telecom/metrics/ApiStats.java
+++ b/src/com/android/server/telecom/metrics/ApiStats.java
@@ -18,10 +18,12 @@
 
 import static com.android.server.telecom.TelecomStatsLog.TELECOM_API_STATS;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.StatsManager;
 import android.content.Context;
 import android.os.Looper;
+import android.telecom.Log;
 import android.util.StatsEvent;
 
 import androidx.annotation.VisibleForTesting;
@@ -29,6 +31,8 @@
 import com.android.server.telecom.TelecomStatsLog;
 import com.android.server.telecom.nano.PulledAtomsClass;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -36,9 +40,131 @@
 import java.util.Objects;
 
 public class ApiStats extends TelecomPulledAtom {
-
+    public static final int API_UNSPECIFIC = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__UNSPECIFIED;
+    public static final int API_ACCEPTHANDOVER = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__ACCEPT_HANDOVER;
+    public static final int API_ACCEPTRINGINGCALL = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__ACCEPT_RINGING_CALL;
+    public static final int API_ACCEPTRINGINGCALLWITHVIDEOSTATE = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__ACCEPT_RINGING_CALL_WITH_VIDEO_STATE;
+    public static final int API_ADDCALL = TelecomStatsLog.TELECOM_API_STATS__API_NAME__ADD_CALL;
+    public static final int API_ADDNEWINCOMINGCALL = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__ADD_NEW_INCOMING_CALL;
+    public static final int API_ADDNEWINCOMINGCONFERENCE = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__ADD_NEW_INCOMING_CONFERENCE;
+    public static final int API_ADDNEWUNKNOWNCALL = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__ADD_NEW_UNKNOWN_CALL;
+    public static final int API_CANCELMISSEDCALLSNOTIFICATION = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__CANCEL_MISSED_CALLS_NOTIFICATION;
+    public static final int API_CLEARACCOUNTS = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__CLEAR_ACCOUNTS;
+    public static final int API_CREATELAUNCHEMERGENCYDIALERINTENT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__CREATE_LAUNCH_EMERGENCY_DIALER_INTENT;
+    public static final int API_CREATEMANAGEBLOCKEDNUMBERSINTENT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__CREATE_MANAGE_BLOCKED_NUMBERS_INTENT;
+    public static final int API_DUMP = TelecomStatsLog.TELECOM_API_STATS__API_NAME__DUMP;
+    public static final int API_DUMPCALLANALYTICS = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__DUMP_CALL_ANALYTICS;
+    public static final int API_ENABLEPHONEACCOUNT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__ENABLE_PHONE_ACCOUNT;
+    public static final int API_ENDCALL = TelecomStatsLog.TELECOM_API_STATS__API_NAME__END_CALL;
+    public static final int API_GETADNURIFORPHONEACCOUNT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_ADN_URI_FOR_PHONE_ACCOUNT;
+    public static final int API_GETALLPHONEACCOUNTHANDLES = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_ALL_PHONE_ACCOUNT_HANDLES;
+    public static final int API_GETALLPHONEACCOUNTS = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_ALL_PHONE_ACCOUNTS;
+    public static final int API_GETALLPHONEACCOUNTSCOUNT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_ALL_PHONE_ACCOUNTS_COUNT;
+    public static final int API_GETCALLCAPABLEPHONEACCOUNTS = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_CALL_CAPABLE_PHONE_ACCOUNTS;
+    public static final int API_GETCALLSTATE = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_CALL_STATE;
+    public static final int API_GETCALLSTATEUSINGPACKAGE = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_CALL_STATE_USING_PACKAGE;
+    public static final int API_GETCURRENTTTYMODE = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_CURRENT_TTY_MODE;
+    public static final int API_GETDEFAULTDIALERPACKAGE = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_DEFAULT_DIALER_PACKAGE;
+    public static final int API_GETDEFAULTDIALERPACKAGEFORUSER = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_DEFAULT_DIALER_PACKAGE_FOR_USER;
+    public static final int API_GETDEFAULTOUTGOINGPHONEACCOUNT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_DEFAULT_OUTGOING_PHONE_ACCOUNT;
+    public static final int API_GETDEFAULTPHONEAPP = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_DEFAULT_PHONE_APP;
+    public static final int API_GETLINE1NUMBER = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_LINE1_NUMBER;
+    public static final int API_GETOWNSELFMANAGEDPHONEACCOUNTS = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_OWN_SELF_MANAGED_PHONE_ACCOUNTS;
+    public static final int API_GETPHONEACCOUNT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_PHONE_ACCOUNT;
+    public static final int API_GETPHONEACCOUNTSFORPACKAGE = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_PHONE_ACCOUNTS_FOR_PACKAGE;
+    public static final int API_GETPHONEACCOUNTSSUPPORTINGSCHEME = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_PHONE_ACCOUNTS_SUPPORTING_SCHEME;
+    public static final int API_GETREGISTEREDPHONEACCOUNTS = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_REGISTERED_PHONE_ACCOUNTS;
+    public static final int API_GETSELFMANAGEDPHONEACCOUNTS = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_SELF_MANAGED_PHONE_ACCOUNTS;
+    public static final int API_GETSIMCALLMANAGER = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_SIM_CALL_MANAGER;
+    public static final int API_GETSIMCALLMANAGERFORUSER = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_SIM_CALL_MANAGER_FOR_USER;
+    public static final int API_GETSYSTEMDIALERPACKAGE = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_SYSTEM_DIALER_PACKAGE;
+    public static final int API_GETUSERSELECTEDOUTGOINGPHONEACCOUNT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT;
+    public static final int API_GETVOICEMAILNUMBER = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__GET_VOICE_MAIL_NUMBER;
+    public static final int API_HANDLEPINMMI = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__HANDLE_PIN_MMI;
+    public static final int API_HANDLEPINMMIFORPHONEACCOUNT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__HANDLE_PIN_MMI_FOR_PHONE_ACCOUNT;
+    public static final int API_HASMANAGEONGOINGCALLSPERMISSION = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__HAS_MANAGE_ONGOING_CALLS_PERMISSION;
+    public static final int API_ISINCALL = TelecomStatsLog.TELECOM_API_STATS__API_NAME__IS_IN_CALL;
+    public static final int API_ISINCOMINGCALLPERMITTED = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__IS_IN_EMERGENCY_CALL;
+    public static final int API_ISINEMERGENCYCALL = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__IS_IN_MANAGED_CALL;
+    public static final int API_ISINMANAGEDCALL = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__IS_IN_SELF_MANAGED_CALL;
+    public static final int API_ISINSELFMANAGEDCALL = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__IS_INCOMING_CALL_PERMITTED;
+    public static final int API_ISOUTGOINGCALLPERMITTED = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__IS_OUTGOING_CALL_PERMITTED;
+    public static final int API_ISRINGING = TelecomStatsLog.TELECOM_API_STATS__API_NAME__IS_RINGING;
+    public static final int API_ISTTYSUPPORTED = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__IS_TTY_SUPPORTED;
+    public static final int API_ISVOICEMAILNUMBER = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__IS_VOICE_MAIL_NUMBER;
+    public static final int API_PLACECALL = TelecomStatsLog.TELECOM_API_STATS__API_NAME__PLACE_CALL;
+    public static final int API_REGISTERPHONEACCOUNT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__REGISTER_PHONE_ACCOUNT;
+    public static final int API_SETDEFAULTDIALER = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__SET_DEFAULT_DIALER;
+    public static final int API_SETUSERSELECTEDOUTGOINGPHONEACCOUNT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__SET_USER_SELECTED_OUTGOING_PHONE_ACCOUNT;
+    public static final int API_SHOWINCALLSCREEN = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__SHOW_IN_CALL_SCREEN;
+    public static final int API_SILENCERINGER = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__SILENCE_RINGER;
+    public static final int API_STARTCONFERENCE = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__START_CONFERENCE;
+    public static final int API_UNREGISTERPHONEACCOUNT = TelecomStatsLog
+            .TELECOM_API_STATS__API_NAME__UNREGISTER_PHONE_ACCOUNT;
+    public static final int RESULT_UNKNOWN = TelecomStatsLog
+            .TELECOM_API_STATS__API_RESULT__RESULT_UNKNOWN;
+    public static final int RESULT_NORMAL = TelecomStatsLog
+            .TELECOM_API_STATS__API_RESULT__RESULT_SUCCESS;
+    public static final int RESULT_PERMISSION = TelecomStatsLog
+            .TELECOM_API_STATS__API_RESULT__RESULT_PERMISSION;
+    public static final int RESULT_EXCEPTION = TelecomStatsLog
+            .TELECOM_API_STATS__API_RESULT__RESULT_EXCEPTION;
+    private static final String TAG = ApiStats.class.getSimpleName();
     private static final String FILE_NAME = "api_stats";
-    private Map<ApiStatsKey, Integer> mApiStatsMap;
+    private Map<ApiEvent, Integer> mApiStatsMap;
 
     public ApiStats(@NonNull Context context, @NonNull Looper looper) {
         super(context, looper);
@@ -73,7 +199,7 @@
         if (mPulledAtoms.telecomApiStats != null) {
             mApiStatsMap = new HashMap<>();
             for (PulledAtomsClass.TelecomApiStats v : mPulledAtoms.telecomApiStats) {
-                mApiStatsMap.put(new ApiStatsKey(v.getApiName(), v.getUid(), v.getApiResult()),
+                mApiStatsMap.put(new ApiEvent(v.getApiName(), v.getUid(), v.getApiResult()),
                         v.getCount());
             }
             mLastPulledTimestamps = mPulledAtoms.getTelecomApiStatsPullTimestampMillis();
@@ -83,6 +209,7 @@
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     @Override
     public synchronized void onAggregate() {
+        Log.d(TAG, "onAggregate: %s", mApiStatsMap);
         clearAtoms();
         if (mApiStatsMap.isEmpty()) {
             return;
@@ -93,7 +220,7 @@
         int[] index = new int[1];
         mApiStatsMap.forEach((k, v) -> {
             mPulledAtoms.telecomApiStats[index[0]] = new PulledAtomsClass.TelecomApiStats();
-            mPulledAtoms.telecomApiStats[index[0]].setApiName(k.mApiId);
+            mPulledAtoms.telecomApiStats[index[0]].setApiName(k.mId);
             mPulledAtoms.telecomApiStats[index[0]].setUid(k.mCallerUid);
             mPulledAtoms.telecomApiStats[index[0]].setApiResult(k.mResult);
             mPulledAtoms.telecomApiStats[index[0]].setCount(v);
@@ -102,46 +229,131 @@
         save(DELAY_FOR_PERSISTENT_MILLIS);
     }
 
-    public void log(int apiId, int callerUid, int result) {
+    public void log(@NonNull ApiEvent event) {
         post(() -> {
-            ApiStatsKey key = new ApiStatsKey(apiId, callerUid, result);
-            mApiStatsMap.put(key, mApiStatsMap.getOrDefault(key, 0) + 1);
+            mApiStatsMap.put(event, mApiStatsMap.getOrDefault(event, 0) + 1);
             onAggregate();
         });
     }
 
-    static class ApiStatsKey {
+    @IntDef(prefix = "API", value = {
+            API_UNSPECIFIC,
+            API_ACCEPTHANDOVER,
+            API_ACCEPTRINGINGCALL,
+            API_ACCEPTRINGINGCALLWITHVIDEOSTATE,
+            API_ADDCALL,
+            API_ADDNEWINCOMINGCALL,
+            API_ADDNEWINCOMINGCONFERENCE,
+            API_ADDNEWUNKNOWNCALL,
+            API_CANCELMISSEDCALLSNOTIFICATION,
+            API_CLEARACCOUNTS,
+            API_CREATELAUNCHEMERGENCYDIALERINTENT,
+            API_CREATEMANAGEBLOCKEDNUMBERSINTENT,
+            API_DUMP,
+            API_DUMPCALLANALYTICS,
+            API_ENABLEPHONEACCOUNT,
+            API_ENDCALL,
+            API_GETADNURIFORPHONEACCOUNT,
+            API_GETALLPHONEACCOUNTHANDLES,
+            API_GETALLPHONEACCOUNTS,
+            API_GETALLPHONEACCOUNTSCOUNT,
+            API_GETCALLCAPABLEPHONEACCOUNTS,
+            API_GETCALLSTATE,
+            API_GETCALLSTATEUSINGPACKAGE,
+            API_GETCURRENTTTYMODE,
+            API_GETDEFAULTDIALERPACKAGE,
+            API_GETDEFAULTDIALERPACKAGEFORUSER,
+            API_GETDEFAULTOUTGOINGPHONEACCOUNT,
+            API_GETDEFAULTPHONEAPP,
+            API_GETLINE1NUMBER,
+            API_GETOWNSELFMANAGEDPHONEACCOUNTS,
+            API_GETPHONEACCOUNT,
+            API_GETPHONEACCOUNTSFORPACKAGE,
+            API_GETPHONEACCOUNTSSUPPORTINGSCHEME,
+            API_GETREGISTEREDPHONEACCOUNTS,
+            API_GETSELFMANAGEDPHONEACCOUNTS,
+            API_GETSIMCALLMANAGER,
+            API_GETSIMCALLMANAGERFORUSER,
+            API_GETSYSTEMDIALERPACKAGE,
+            API_GETUSERSELECTEDOUTGOINGPHONEACCOUNT,
+            API_GETVOICEMAILNUMBER,
+            API_HANDLEPINMMI,
+            API_HANDLEPINMMIFORPHONEACCOUNT,
+            API_HASMANAGEONGOINGCALLSPERMISSION,
+            API_ISINCALL,
+            API_ISINCOMINGCALLPERMITTED,
+            API_ISINEMERGENCYCALL,
+            API_ISINMANAGEDCALL,
+            API_ISINSELFMANAGEDCALL,
+            API_ISOUTGOINGCALLPERMITTED,
+            API_ISRINGING,
+            API_ISTTYSUPPORTED,
+            API_ISVOICEMAILNUMBER,
+            API_PLACECALL,
+            API_REGISTERPHONEACCOUNT,
+            API_SETDEFAULTDIALER,
+            API_SETUSERSELECTEDOUTGOINGPHONEACCOUNT,
+            API_SHOWINCALLSCREEN,
+            API_SILENCERINGER,
+            API_STARTCONFERENCE,
+            API_UNREGISTERPHONEACCOUNT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApiId {
+    }
 
-        int mApiId;
+    @IntDef(prefix = "RESULT", value = {
+            RESULT_UNKNOWN,
+            RESULT_NORMAL,
+            RESULT_PERMISSION,
+            RESULT_EXCEPTION,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ResultId {
+    }
+
+    public static class ApiEvent {
+
+        @ApiId
+        int mId;
         int mCallerUid;
+        @ResultId
         int mResult;
 
-        ApiStatsKey(int apiId, int callerUid, int result) {
-            mApiId = apiId;
+        public ApiEvent(@ApiId int id, int callerUid, @ResultId int result) {
+            mId = id;
             mCallerUid = callerUid;
             mResult = result;
         }
 
+        public void setCallerUid(int uid) {
+            this.mCallerUid = uid;
+        }
+
+        public void setResult(@ResultId int result) {
+            this.mResult = result;
+        }
+
         @Override
         public boolean equals(Object other) {
             if (this == other) {
                 return true;
             }
-            if (other == null || !(other instanceof ApiStatsKey obj)) {
+            if (!(other instanceof ApiEvent obj)) {
                 return false;
             }
-            return this.mApiId == obj.mApiId && this.mCallerUid == obj.mCallerUid
+            return this.mId == obj.mId && this.mCallerUid == obj.mCallerUid
                     && this.mResult == obj.mResult;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mApiId, mCallerUid, mResult);
+            return Objects.hash(mId, mCallerUid, mResult);
         }
 
         @Override
         public String toString() {
-            return "[ApiStatsKey: mApiId=" + mApiId + ", mCallerUid=" + mCallerUid
+            return "[ApiEvent: mApiId=" + mId + ", mCallerUid=" + mCallerUid
                     + ", mResult=" + mResult + "]";
         }
     }
diff --git a/src/com/android/server/telecom/metrics/CallStats.java b/src/com/android/server/telecom/metrics/CallStats.java
index 39b0e6d..17461da 100644
--- a/src/com/android/server/telecom/metrics/CallStats.java
+++ b/src/com/android/server/telecom/metrics/CallStats.java
@@ -165,6 +165,9 @@
     }
 
     private int getAccountType(PhoneAccount account) {
+        if (account == null) {
+            return CALL_STATS__ACCOUNT_TYPE__ACCOUNT_UNKNOWN;
+        }
         if (account.hasCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED)) {
             return account.hasCapabilities(
                     PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS)
diff --git a/src/com/android/server/telecom/metrics/ErrorStats.java b/src/com/android/server/telecom/metrics/ErrorStats.java
index e4d0a51..f70f6d8 100644
--- a/src/com/android/server/telecom/metrics/ErrorStats.java
+++ b/src/com/android/server/telecom/metrics/ErrorStats.java
@@ -18,10 +18,12 @@
 
 import static com.android.server.telecom.TelecomStatsLog.TELECOM_ERROR_STATS;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.StatsManager;
 import android.content.Context;
 import android.os.Looper;
+import android.telecom.Log;
 import android.util.StatsEvent;
 
 import androidx.annotation.VisibleForTesting;
@@ -29,6 +31,8 @@
 import com.android.server.telecom.TelecomStatsLog;
 import com.android.server.telecom.nano.PulledAtomsClass;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -36,9 +40,83 @@
 import java.util.Objects;
 
 public class ErrorStats extends TelecomPulledAtom {
-
+    public static final int SUB_UNKNOWN = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_UNKNOWN;
+    public static final int SUB_CALL_AUDIO = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_CALL_AUDIO;
+    public static final int SUB_CALL_LOGS = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_CALL_LOGS;
+    public static final int SUB_CALL_MANAGER = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_CALL_MANAGER;
+    public static final int SUB_CONNECTION_SERVICE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_CONNECTION_SERVICE;
+    public static final int SUB_EMERGENCY_CALL = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_EMERGENCY_CALL;
+    public static final int SUB_IN_CALL_SERVICE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_IN_CALL_SERVICE;
+    public static final int SUB_MISC = TelecomStatsLog.TELECOM_ERROR_STATS__SUBMODULE__SUB_MISC;
+    public static final int SUB_PHONE_ACCOUNT = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_PHONE_ACCOUNT;
+    public static final int SUB_SYSTEM_SERVICE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_SYSTEM_SERVICE;
+    public static final int SUB_TELEPHONY = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_TELEPHONY;
+    public static final int SUB_UI = TelecomStatsLog.TELECOM_ERROR_STATS__SUBMODULE__SUB_UI;
+    public static final int SUB_VOIP_CALL = TelecomStatsLog
+            .TELECOM_ERROR_STATS__SUBMODULE__SUB_VOIP_CALL;
+    public static final int ERROR_UNKNOWN = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_UNKNOWN;
+    public static final int ERROR_EXTERNAL_EXCEPTION = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_EXTERNAL_EXCEPTION;
+    public static final int ERROR_INTERNAL_EXCEPTION = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_INTERNAL_EXCEPTION;
+    public static final int ERROR_AUDIO_ROUTE_RETRY_REJECTED = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_AUDIO_ROUTE_RETRY_REJECTED;
+    public static final int ERROR_BT_GET_SERVICE_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_BT_GET_SERVICE_FAILURE;
+    public static final int ERROR_BT_REGISTER_CALLBACK_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_BT_REGISTER_CALLBACK_FAILURE;
+    public static final int ERROR_AUDIO_ROUTE_UNAVAILABLE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_AUDIO_ROUTE_UNAVAILABLE;
+    public static final int ERROR_EMERGENCY_NUMBER_DETERMINED_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_EMERGENCY_NUMBER_DETERMINED_FAILURE;
+    public static final int ERROR_NOTIFY_CALL_STREAM_START_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_NOTIFY_CALL_STREAM_START_FAILURE;
+    public static final int ERROR_NOTIFY_CALL_STREAM_STATE_CHANGED_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_NOTIFY_CALL_STREAM_STATE_CHANGED_FAILURE;
+    public static final int ERROR_NOTIFY_CALL_STREAM_STOP_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_NOTIFY_CALL_STREAM_STOP_FAILURE;
+    public static final int ERROR_RTT_STREAM_CLOSE_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_RTT_STREAM_CLOSE_FAILURE;
+    public static final int ERROR_RTT_STREAM_CREATE_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_RTT_STREAM_CREATE_FAILURE;
+    public static final int ERROR_SET_MUTED_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_SET_MUTED_FAILURE;
+    public static final int ERROR_VIDEO_PROVIDER_SET_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_VIDEO_PROVIDER_SET_FAILURE;
+    public static final int ERROR_WIRED_HEADSET_NOT_AVAILABLE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_WIRED_HEADSET_NOT_AVAILABLE;
+    public static final int ERROR_LOG_CALL_FAILURE = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_LOG_CALL_FAILURE;
+    public static final int ERROR_RETRIEVING_ACCOUNT_EMERGENCY = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_RETRIEVING_ACCOUNT_EMERGENCY;
+    public static final int ERROR_RETRIEVING_ACCOUNT = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_RETRIEVING_ACCOUNT;
+    public static final int ERROR_EMERGENCY_CALL_ABORTED_NO_ACCOUNT = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_EMERGENCY_CALL_ABORTED_NO_ACCOUNT;
+    public static final int ERROR_DEFAULT_MO_ACCOUNT_MISMATCH = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_DEFAULT_MO_ACCOUNT_MISMATCH;
+    public static final int ERROR_ESTABLISHING_CONNECTION = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_ESTABLISHING_CONNECTION;
+    public static final int ERROR_REMOVING_CALL = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_REMOVING_CALL;
+    public static final int ERROR_STUCK_CONNECTING_EMERGENCY = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_STUCK_CONNECTING_EMERGENCY;
+    public static final int ERROR_STUCK_CONNECTING = TelecomStatsLog
+            .TELECOM_ERROR_STATS__ERROR__ERROR_STUCK_CONNECTING;
+    private static final String TAG = ErrorStats.class.getSimpleName();
     private static final String FILE_NAME = "error_stats";
-    private Map<ErrorStatsKey, Integer> mErrorStatsMap;
+    private Map<ErrorEvent, Integer> mErrorStatsMap;
 
     public ErrorStats(@NonNull Context context, @NonNull Looper looper) {
         super(context, looper);
@@ -61,7 +139,7 @@
         if (mPulledAtoms.telecomErrorStats.length != 0) {
             Arrays.stream(mPulledAtoms.telecomErrorStats).forEach(v -> data.add(
                     TelecomStatsLog.buildStatsEvent(getTag(),
-                            v.getSubmoduleName(), v.getErrorName(), v.getCount())));
+                            v.getSubmodule(), v.getError(), v.getCount())));
             return StatsManager.PULL_SUCCESS;
         } else {
             return StatsManager.PULL_SKIP;
@@ -73,7 +151,7 @@
         if (mPulledAtoms.telecomErrorStats != null) {
             mErrorStatsMap = new HashMap<>();
             for (PulledAtomsClass.TelecomErrorStats v : mPulledAtoms.telecomErrorStats) {
-                mErrorStatsMap.put(new ErrorStatsKey(v.getSubmoduleName(), v.getErrorName()),
+                mErrorStatsMap.put(new ErrorEvent(v.getSubmodule(), v.getError()),
                         v.getCount());
             }
             mLastPulledTimestamps = mPulledAtoms.getTelecomErrorStatsPullTimestampMillis();
@@ -83,6 +161,7 @@
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     @Override
     public synchronized void onAggregate() {
+        Log.d(TAG, "onAggregate: %s", mErrorStatsMap);
         clearAtoms();
         if (mErrorStatsMap.isEmpty()) {
             return;
@@ -93,28 +172,78 @@
         int[] index = new int[1];
         mErrorStatsMap.forEach((k, v) -> {
             mPulledAtoms.telecomErrorStats[index[0]] = new PulledAtomsClass.TelecomErrorStats();
-            mPulledAtoms.telecomErrorStats[index[0]].setSubmoduleName(k.mModuleId);
-            mPulledAtoms.telecomErrorStats[index[0]].setErrorName(k.mErrorId);
+            mPulledAtoms.telecomErrorStats[index[0]].setSubmodule(k.mModuleId);
+            mPulledAtoms.telecomErrorStats[index[0]].setError(k.mErrorId);
             mPulledAtoms.telecomErrorStats[index[0]].setCount(v);
             index[0]++;
         });
         save(DELAY_FOR_PERSISTENT_MILLIS);
     }
 
-    public void log(int moduleId, int errorId) {
+    public void log(@SubModuleId int moduleId, @ErrorId int errorId) {
         post(() -> {
-            ErrorStatsKey key = new ErrorStatsKey(moduleId, errorId);
+            ErrorEvent key = new ErrorEvent(moduleId, errorId);
             mErrorStatsMap.put(key, mErrorStatsMap.getOrDefault(key, 0) + 1);
             onAggregate();
         });
     }
 
-    static class ErrorStatsKey {
+    @IntDef(prefix = "SUB", value = {
+            SUB_UNKNOWN,
+            SUB_CALL_AUDIO,
+            SUB_CALL_LOGS,
+            SUB_CALL_MANAGER,
+            SUB_CONNECTION_SERVICE,
+            SUB_EMERGENCY_CALL,
+            SUB_IN_CALL_SERVICE,
+            SUB_MISC,
+            SUB_PHONE_ACCOUNT,
+            SUB_SYSTEM_SERVICE,
+            SUB_TELEPHONY,
+            SUB_UI,
+            SUB_VOIP_CALL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SubModuleId {
+    }
 
-        final int mModuleId;
-        final int mErrorId;
+    @IntDef(prefix = "ERROR", value = {
+            ERROR_UNKNOWN,
+            ERROR_EXTERNAL_EXCEPTION,
+            ERROR_INTERNAL_EXCEPTION,
+            ERROR_AUDIO_ROUTE_RETRY_REJECTED,
+            ERROR_BT_GET_SERVICE_FAILURE,
+            ERROR_BT_REGISTER_CALLBACK_FAILURE,
+            ERROR_AUDIO_ROUTE_UNAVAILABLE,
+            ERROR_EMERGENCY_NUMBER_DETERMINED_FAILURE,
+            ERROR_NOTIFY_CALL_STREAM_START_FAILURE,
+            ERROR_NOTIFY_CALL_STREAM_STATE_CHANGED_FAILURE,
+            ERROR_NOTIFY_CALL_STREAM_STOP_FAILURE,
+            ERROR_RTT_STREAM_CLOSE_FAILURE,
+            ERROR_RTT_STREAM_CREATE_FAILURE,
+            ERROR_SET_MUTED_FAILURE,
+            ERROR_VIDEO_PROVIDER_SET_FAILURE,
+            ERROR_WIRED_HEADSET_NOT_AVAILABLE,
+            ERROR_LOG_CALL_FAILURE,
+            ERROR_RETRIEVING_ACCOUNT_EMERGENCY,
+            ERROR_RETRIEVING_ACCOUNT,
+            ERROR_EMERGENCY_CALL_ABORTED_NO_ACCOUNT,
+            ERROR_DEFAULT_MO_ACCOUNT_MISMATCH,
+            ERROR_ESTABLISHING_CONNECTION,
+            ERROR_REMOVING_CALL,
+            ERROR_STUCK_CONNECTING_EMERGENCY,
+            ERROR_STUCK_CONNECTING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ErrorId {
+    }
 
-        ErrorStatsKey(int moduleId, int errorId) {
+    static class ErrorEvent {
+
+        final @SubModuleId int mModuleId;
+        final @ErrorId int mErrorId;
+
+        ErrorEvent(@SubModuleId int moduleId, @ErrorId int errorId) {
             mModuleId = moduleId;
             mErrorId = errorId;
         }
@@ -124,7 +253,7 @@
             if (this == other) {
                 return true;
             }
-            if (!(other instanceof ErrorStatsKey obj)) {
+            if (!(other instanceof ErrorEvent obj)) {
                 return false;
             }
             return this.mModuleId == obj.mModuleId && this.mErrorId == obj.mErrorId;
@@ -137,7 +266,7 @@
 
         @Override
         public String toString() {
-            return "[ErrorStatsKey: mModuleId=" + mModuleId + ", mErrorId=" + mErrorId + "]";
+            return "[ErrorEvent: mModuleId=" + mModuleId + ", mErrorId=" + mErrorId + "]";
         }
     }
 }
diff --git a/src/com/android/server/telecom/metrics/TelecomMetricsController.java b/src/com/android/server/telecom/metrics/TelecomMetricsController.java
index 8903b02..1516ddd 100644
--- a/src/com/android/server/telecom/metrics/TelecomMetricsController.java
+++ b/src/com/android/server/telecom/metrics/TelecomMetricsController.java
@@ -51,7 +51,7 @@
 
     @NonNull
     public static TelecomMetricsController make(@NonNull Context context) {
-        Log.i(TAG, "TMC.iN1");
+        Log.i(TAG, "TMC.m1");
         HandlerThread handlerThread = new HandlerThread(TAG);
         handlerThread.start();
         return make(context, handlerThread);
@@ -61,7 +61,7 @@
     @NonNull
     public static TelecomMetricsController make(@NonNull Context context,
                                                 @NonNull HandlerThread handlerThread) {
-        Log.i(TAG, "TMC.iN2");
+        Log.i(TAG, "TMC.m2");
         Objects.requireNonNull(context);
         Objects.requireNonNull(handlerThread);
         return new TelecomMetricsController(context, handlerThread);
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index 25f94c6..37221d8 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -301,6 +301,8 @@
                 return Context.TELECOM_SERVICE;
             } else if (svcClass == BlockedNumbersManager.class) {
                 return Context.BLOCKED_NUMBERS_SERVICE;
+            } else if (svcClass == AppOpsManager.class) {
+                return Context.APP_OPS_SERVICE;
             }
             throw new UnsupportedOperationException(svcClass.getName());
         }
diff --git a/tests/src/com/android/server/telecom/tests/TelecomPulledAtomTest.java b/tests/src/com/android/server/telecom/tests/TelecomPulledAtomTest.java
index bc8aeac..528b525 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomPulledAtomTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomPulledAtomTest.java
@@ -66,7 +66,10 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Random;
 
 @RunWith(AndroidJUnit4.class)
 public class TelecomPulledAtomTest extends TelecomTestCase {
@@ -288,26 +291,108 @@
     }
 
     @Test
-    public void testApiStatsLog() throws Exception {
+    public void testApiStatsLogCount() throws Exception {
         ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper));
+        ApiStats.ApiEvent event = new ApiStats.ApiEvent(VALUE_API_ID, VALUE_UID, VALUE_API_RESULT);
 
-        apiStats.log(VALUE_API_ID, VALUE_UID, VALUE_API_RESULT);
-        waitForHandlerAction(apiStats, TEST_TIMEOUT);
+        for (int i = 0; i < 10; i++) {
+            apiStats.log(event);
+            waitForHandlerAction(apiStats, TEST_TIMEOUT);
 
-        verify(apiStats, times(1)).onAggregate();
-        verify(apiStats, times(1)).save(eq(DELAY_FOR_PERSISTENT_MILLIS));
-        assertEquals(apiStats.mPulledAtoms.telecomApiStats.length, 1);
-        verifyMessageForApiStats(apiStats.mPulledAtoms.telecomApiStats[0], VALUE_API_ID,
-                VALUE_UID, VALUE_API_RESULT, 1);
+            verify(apiStats, times(i + 1)).onAggregate();
+            verify(apiStats, times(i + 1)).save(eq(DELAY_FOR_PERSISTENT_MILLIS));
+            assertEquals(apiStats.mPulledAtoms.telecomApiStats.length, 1);
+            verifyMessageForApiStats(apiStats.mPulledAtoms.telecomApiStats[0], VALUE_API_ID,
+                    VALUE_UID, VALUE_API_RESULT, i + 1);
+        }
+    }
 
-        apiStats.log(VALUE_API_ID, VALUE_UID, VALUE_API_RESULT);
-        waitForHandlerAction(apiStats, TEST_TIMEOUT);
+    @Test
+    public void testApiStatsLogEvent() throws Exception {
+        final int[] apis = {
+                ApiStats.API_UNSPECIFIC,
+                ApiStats.API_ACCEPTHANDOVER,
+                ApiStats.API_ACCEPTRINGINGCALL,
+                ApiStats.API_ACCEPTRINGINGCALLWITHVIDEOSTATE,
+                ApiStats.API_ADDCALL,
+                ApiStats.API_ADDNEWINCOMINGCALL,
+                ApiStats.API_ADDNEWINCOMINGCONFERENCE,
+                ApiStats.API_ADDNEWUNKNOWNCALL,
+                ApiStats.API_CANCELMISSEDCALLSNOTIFICATION,
+                ApiStats.API_CLEARACCOUNTS,
+                ApiStats.API_CREATELAUNCHEMERGENCYDIALERINTENT,
+                ApiStats.API_CREATEMANAGEBLOCKEDNUMBERSINTENT,
+                ApiStats.API_DUMP,
+                ApiStats.API_DUMPCALLANALYTICS,
+                ApiStats.API_ENABLEPHONEACCOUNT,
+                ApiStats.API_ENDCALL,
+                ApiStats.API_GETADNURIFORPHONEACCOUNT,
+                ApiStats.API_GETALLPHONEACCOUNTHANDLES,
+                ApiStats.API_GETALLPHONEACCOUNTS,
+                ApiStats.API_GETALLPHONEACCOUNTSCOUNT,
+                ApiStats.API_GETCALLCAPABLEPHONEACCOUNTS,
+                ApiStats.API_GETCALLSTATE,
+                ApiStats.API_GETCALLSTATEUSINGPACKAGE,
+                ApiStats.API_GETCURRENTTTYMODE,
+                ApiStats.API_GETDEFAULTDIALERPACKAGE,
+                ApiStats.API_GETDEFAULTDIALERPACKAGEFORUSER,
+                ApiStats.API_GETDEFAULTOUTGOINGPHONEACCOUNT,
+                ApiStats.API_GETDEFAULTPHONEAPP,
+                ApiStats.API_GETLINE1NUMBER,
+                ApiStats.API_GETOWNSELFMANAGEDPHONEACCOUNTS,
+                ApiStats.API_GETPHONEACCOUNT,
+                ApiStats.API_GETPHONEACCOUNTSFORPACKAGE,
+                ApiStats.API_GETPHONEACCOUNTSSUPPORTINGSCHEME,
+                ApiStats.API_GETREGISTEREDPHONEACCOUNTS,
+                ApiStats.API_GETSELFMANAGEDPHONEACCOUNTS,
+                ApiStats.API_GETSIMCALLMANAGER,
+                ApiStats.API_GETSIMCALLMANAGERFORUSER,
+                ApiStats.API_GETSYSTEMDIALERPACKAGE,
+                ApiStats.API_GETUSERSELECTEDOUTGOINGPHONEACCOUNT,
+                ApiStats.API_GETVOICEMAILNUMBER,
+                ApiStats.API_HANDLEPINMMI,
+                ApiStats.API_HANDLEPINMMIFORPHONEACCOUNT,
+                ApiStats.API_HASMANAGEONGOINGCALLSPERMISSION,
+                ApiStats.API_ISINCALL,
+                ApiStats.API_ISINCOMINGCALLPERMITTED,
+                ApiStats.API_ISINEMERGENCYCALL,
+                ApiStats.API_ISINMANAGEDCALL,
+                ApiStats.API_ISINSELFMANAGEDCALL,
+                ApiStats.API_ISOUTGOINGCALLPERMITTED,
+                ApiStats.API_ISRINGING,
+                ApiStats.API_ISTTYSUPPORTED,
+                ApiStats.API_ISVOICEMAILNUMBER,
+                ApiStats.API_PLACECALL,
+                ApiStats.API_REGISTERPHONEACCOUNT,
+                ApiStats.API_SETDEFAULTDIALER,
+                ApiStats.API_SETUSERSELECTEDOUTGOINGPHONEACCOUNT,
+                ApiStats.API_SHOWINCALLSCREEN,
+                ApiStats.API_SILENCERINGER,
+                ApiStats.API_STARTCONFERENCE,
+                ApiStats.API_UNREGISTERPHONEACCOUNT,
+        };
+        final int[] results = {ApiStats.RESULT_UNKNOWN, ApiStats.RESULT_NORMAL,
+                ApiStats.RESULT_EXCEPTION, ApiStats.RESULT_PERMISSION};
+        ApiStats apiStats = spy(new ApiStats(mSpyContext, mLooper));
+        Random rand = new Random();
+        Map<ApiStats.ApiEvent, Integer> eventMap = new HashMap<>();
 
-        verify(apiStats, times(2)).onAggregate();
-        verify(apiStats, times(2)).save(eq(DELAY_FOR_PERSISTENT_MILLIS));
-        assertEquals(apiStats.mPulledAtoms.telecomApiStats.length, 1);
-        verifyMessageForApiStats(apiStats.mPulledAtoms.telecomApiStats[0], VALUE_API_ID,
-                VALUE_UID, VALUE_API_RESULT, 2);
+        for (int i = 0; i < 10; i++) {
+            int api = apis[rand.nextInt(apis.length)];
+            int uid = rand.nextInt(65535);
+            int result = results[rand.nextInt(results.length)];
+            ApiStats.ApiEvent event = new ApiStats.ApiEvent(api, uid, result);
+            eventMap.put(event, eventMap.getOrDefault(event, 0) + 1);
+
+            apiStats.log(event);
+            waitForHandlerAction(apiStats, TEST_TIMEOUT);
+
+            verify(apiStats, times(i + 1)).onAggregate();
+            verify(apiStats, times(i + 1)).save(eq(DELAY_FOR_PERSISTENT_MILLIS));
+            assertEquals(apiStats.mPulledAtoms.telecomApiStats.length, eventMap.size());
+            assertTrue(hasMessageForApiStats(apiStats.mPulledAtoms.telecomApiStats,
+                    api, uid, result, eventMap.get(event)));
+        }
     }
 
     @Test
@@ -623,26 +708,84 @@
     }
 
     @Test
-    public void testErrorStatsLog() throws Exception {
+    public void testErrorStatsLogCount() throws Exception {
         ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper));
+        for (int i = 0; i < 10; i++) {
+            errorStats.log(VALUE_MODULE_ID, VALUE_ERROR_ID);
+            waitForHandlerAction(errorStats, TEST_TIMEOUT);
 
-        errorStats.log(VALUE_MODULE_ID, VALUE_ERROR_ID);
-        waitForHandlerAction(errorStats, TEST_TIMEOUT);
+            verify(errorStats, times(i + 1)).onAggregate();
+            verify(errorStats, times(i + 1)).save(eq(DELAY_FOR_PERSISTENT_MILLIS));
+            assertEquals(errorStats.mPulledAtoms.telecomErrorStats.length, 1);
+            verifyMessageForErrorStats(errorStats.mPulledAtoms.telecomErrorStats[0],
+                    VALUE_MODULE_ID,
+                    VALUE_ERROR_ID, i + 1);
+        }
+    }
 
-        verify(errorStats, times(1)).onAggregate();
-        verify(errorStats, times(1)).save(eq(DELAY_FOR_PERSISTENT_MILLIS));
-        assertEquals(errorStats.mPulledAtoms.telecomErrorStats.length, 1);
-        verifyMessageForErrorStats(errorStats.mPulledAtoms.telecomErrorStats[0], VALUE_MODULE_ID,
-                VALUE_ERROR_ID, 1);
+    @Test
+    public void testErrorStatsLogEvent() throws Exception {
+        ErrorStats errorStats = spy(new ErrorStats(mSpyContext, mLooper));
+        int[] modules = {
+                ErrorStats.SUB_UNKNOWN,
+                ErrorStats.SUB_CALL_AUDIO,
+                ErrorStats.SUB_CALL_LOGS,
+                ErrorStats.SUB_CALL_MANAGER,
+                ErrorStats.SUB_CONNECTION_SERVICE,
+                ErrorStats.SUB_EMERGENCY_CALL,
+                ErrorStats.SUB_IN_CALL_SERVICE,
+                ErrorStats.SUB_MISC,
+                ErrorStats.SUB_PHONE_ACCOUNT,
+                ErrorStats.SUB_SYSTEM_SERVICE,
+                ErrorStats.SUB_TELEPHONY,
+                ErrorStats.SUB_UI,
+                ErrorStats.SUB_VOIP_CALL,
+        };
+        int[] errors = {
+                ErrorStats.ERROR_UNKNOWN,
+                ErrorStats.ERROR_EXTERNAL_EXCEPTION,
+                ErrorStats.ERROR_INTERNAL_EXCEPTION,
+                ErrorStats.ERROR_AUDIO_ROUTE_RETRY_REJECTED,
+                ErrorStats.ERROR_BT_GET_SERVICE_FAILURE,
+                ErrorStats.ERROR_BT_REGISTER_CALLBACK_FAILURE,
+                ErrorStats.ERROR_AUDIO_ROUTE_UNAVAILABLE,
+                ErrorStats.ERROR_EMERGENCY_NUMBER_DETERMINED_FAILURE,
+                ErrorStats.ERROR_NOTIFY_CALL_STREAM_START_FAILURE,
+                ErrorStats.ERROR_NOTIFY_CALL_STREAM_STATE_CHANGED_FAILURE,
+                ErrorStats.ERROR_NOTIFY_CALL_STREAM_STOP_FAILURE,
+                ErrorStats.ERROR_RTT_STREAM_CLOSE_FAILURE,
+                ErrorStats.ERROR_RTT_STREAM_CREATE_FAILURE,
+                ErrorStats.ERROR_SET_MUTED_FAILURE,
+                ErrorStats.ERROR_VIDEO_PROVIDER_SET_FAILURE,
+                ErrorStats.ERROR_WIRED_HEADSET_NOT_AVAILABLE,
+                ErrorStats.ERROR_LOG_CALL_FAILURE,
+                ErrorStats.ERROR_RETRIEVING_ACCOUNT_EMERGENCY,
+                ErrorStats.ERROR_RETRIEVING_ACCOUNT,
+                ErrorStats.ERROR_EMERGENCY_CALL_ABORTED_NO_ACCOUNT,
+                ErrorStats.ERROR_DEFAULT_MO_ACCOUNT_MISMATCH,
+                ErrorStats.ERROR_ESTABLISHING_CONNECTION,
+                ErrorStats.ERROR_REMOVING_CALL,
+                ErrorStats.ERROR_STUCK_CONNECTING_EMERGENCY,
+                ErrorStats.ERROR_STUCK_CONNECTING,
+        };
+        Random rand = new Random();
+        Map<Long, Integer> eventMap = new HashMap<>();
 
-        errorStats.log(VALUE_MODULE_ID, VALUE_ERROR_ID);
-        waitForHandlerAction(errorStats, TEST_TIMEOUT);
+        for (int i = 0; i < 10; i++) {
+            int module = modules[rand.nextInt(modules.length)];
+            int error = errors[rand.nextInt(errors.length)];
+            long key = (long) module << 32 | error;
+            eventMap.put(key, eventMap.getOrDefault(key, 0) + 1);
 
-        verify(errorStats, times(2)).onAggregate();
-        verify(errorStats, times(2)).save(eq(DELAY_FOR_PERSISTENT_MILLIS));
-        assertEquals(errorStats.mPulledAtoms.telecomErrorStats.length, 1);
-        verifyMessageForErrorStats(errorStats.mPulledAtoms.telecomErrorStats[0], VALUE_MODULE_ID,
-                VALUE_ERROR_ID, 2);
+            errorStats.log(module, error);
+            waitForHandlerAction(errorStats, DELAY_TOLERANCE);
+
+            verify(errorStats, times(i + 1)).onAggregate();
+            verify(errorStats, times(i + 1)).save(eq(DELAY_FOR_PERSISTENT_MILLIS));
+            assertEquals(errorStats.mPulledAtoms.telecomErrorStats.length, eventMap.size());
+            assertTrue(hasMessageForErrorStats(
+                    errorStats.mPulledAtoms.telecomErrorStats, module, error, eventMap.get(key)));
+        }
     }
 
     private void createTestFileForApiStats(long timestamps) throws IOException {
@@ -664,7 +807,7 @@
     }
 
     private void verifyTestDataForApiStats(final PulledAtomsClass.PulledAtoms atom,
-                                           long timestamps) {
+            long timestamps) {
         assertNotNull(atom);
         assertEquals(atom.getTelecomApiStatsPullTimestampMillis(), timestamps);
         assertNotNull(atom.telecomApiStats);
@@ -677,13 +820,24 @@
     }
 
     private void verifyMessageForApiStats(final PulledAtomsClass.TelecomApiStats msg, int apiId,
-                                          int uid, int result, int count) {
+            int uid, int result, int count) {
         assertEquals(msg.getApiName(), apiId);
         assertEquals(msg.getUid(), uid);
         assertEquals(msg.getApiResult(), result);
         assertEquals(msg.getCount(), count);
     }
 
+    private boolean hasMessageForApiStats(final PulledAtomsClass.TelecomApiStats[] msgs, int apiId,
+            int uid, int result, int count) {
+        for (PulledAtomsClass.TelecomApiStats msg : msgs) {
+            if (msg.getApiName() == apiId && msg.getUid() == uid && msg.getApiResult() == result
+                    && msg.getCount() == count) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void createTestFileForAudioRouteStats(long timestamps) throws IOException {
         PulledAtomsClass.PulledAtoms atom = new PulledAtomsClass.PulledAtoms();
         atom.callAudioRouteStats =
@@ -704,7 +858,7 @@
     }
 
     private void verifyTestDataForAudioRouteStats(final PulledAtomsClass.PulledAtoms atom,
-                                                  long timestamps) {
+            long timestamps) {
         assertNotNull(atom);
         assertEquals(atom.getCallAudioRouteStatsPullTimestampMillis(), timestamps);
         assertNotNull(atom.callAudioRouteStats);
@@ -750,7 +904,7 @@
     }
 
     private void verifyTestDataForCallStats(final PulledAtomsClass.PulledAtoms atom,
-                                            long timestamps) {
+            long timestamps) {
         assertNotNull(atom);
         assertEquals(atom.getCallStatsPullTimestampMillis(), timestamps);
         assertNotNull(atom.callStats);
@@ -782,8 +936,8 @@
                 new PulledAtomsClass.TelecomErrorStats[VALUE_ATOM_COUNT];
         for (int i = 0; i < VALUE_ATOM_COUNT; i++) {
             atom.telecomErrorStats[i] = new PulledAtomsClass.TelecomErrorStats();
-            atom.telecomErrorStats[i].setSubmoduleName(VALUE_MODULE_ID);
-            atom.telecomErrorStats[i].setErrorName(VALUE_ERROR_ID);
+            atom.telecomErrorStats[i].setSubmodule(VALUE_MODULE_ID);
+            atom.telecomErrorStats[i].setError(VALUE_ERROR_ID);
             atom.telecomErrorStats[i].setCount(VALUE_ERROR_COUNT);
         }
         atom.setTelecomErrorStatsPullTimestampMillis(timestamps);
@@ -807,8 +961,19 @@
 
     private void verifyMessageForErrorStats(final PulledAtomsClass.TelecomErrorStats msg,
             int moduleId, int errorId, int count) {
-        assertEquals(msg.getSubmoduleName(), moduleId);
-        assertEquals(msg.getErrorName(), errorId);
+        assertEquals(msg.getSubmodule(), moduleId);
+        assertEquals(msg.getError(), errorId);
         assertEquals(msg.getCount(), count);
     }
+
+    private boolean hasMessageForErrorStats(final PulledAtomsClass.TelecomErrorStats[] msgs,
+            int moduleId, int errorId, int count) {
+        for (PulledAtomsClass.TelecomErrorStats msg : msgs) {
+            if (msg.getSubmodule() == moduleId && msg.getError() == errorId
+                    && msg.getCount() == count) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index dc5f325..793cf14 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -96,6 +96,7 @@
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 import com.android.server.telecom.flags.FeatureFlags;
+import com.android.server.telecom.metrics.TelecomMetricsController;
 import com.android.server.telecom.voip.IncomingCallTransaction;
 import com.android.server.telecom.voip.OutgoingCallTransaction;
 import com.android.server.telecom.voip.TransactionManager;
@@ -204,6 +205,7 @@
     @Mock private com.android.internal.telephony.flags.FeatureFlags mTelephonyFeatureFlags;
 
     @Mock private InCallController mInCallController;
+    @Mock private TelecomMetricsController mMockTelecomMetricsController;
 
     private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
 
@@ -257,7 +259,8 @@
                 mSettingsSecureAdapter,
                 mFeatureFlags,
                 mTelephonyFeatureFlags,
-                mLock);
+                mLock,
+                mMockTelecomMetricsController);
         telecomServiceImpl.setTransactionManager(mTransactionManager);
         telecomServiceImpl.setAnomalyReporterAdapter(mAnomalyReporterAdapter);
         mTSIBinder = telecomServiceImpl.getBinder();