add anomaly reports for every new CallException occurrence
Adding these anomaly reports will help surface the frequency of each
CalException. A total of 8 new anomaly reports have been added in this
CL.
Flag: com.android.server.telecom.flags.enable_call_exception_anom_reports
Fixes: 390654979
Test: build
Change-Id: Ided325ade6c4610e902942b7c57539459ff96930
diff --git a/flags/telecom_anomaly_report_flags.aconfig b/flags/telecom_anomaly_report_flags.aconfig
index 5d42b86..bc248c8 100644
--- a/flags/telecom_anomaly_report_flags.aconfig
+++ b/flags/telecom_anomaly_report_flags.aconfig
@@ -27,3 +27,11 @@
purpose: PURPOSE_BUGFIX
}
}
+
+# OWNER=tjstuart TARGET=25Q2
+flag {
+ name: "enable_call_exception_anom_reports"
+ namespace: "telecom"
+ description: "When a new CallException is created, generate an anomaly report for metrics"
+ bug: "308932906"
+}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 25992fc..0a38720 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -329,6 +329,10 @@
UUID.fromString("0a86157c-50ca-11ee-be56-0242ac120002");
public static final String TELEPHONY_HAS_DEFAULT_BUT_TELECOM_DOES_NOT_MSG =
"Telephony has a default MO acct but Telecom prompted user for MO";
+ public static final UUID CANNOT_HOLD_CURRENT_ACTIVE_CALL_ERROR_UUID =
+ UUID.fromString("1b6a9b88-5049-4ffa-a52a-134d7c3a40e6");
+ public static final UUID FAILED_TO_SWITCH_FOCUS_ERROR_UUID =
+ UUID.fromString("a1b2c3d4-e5f6-7890-1234-567890abcdef");
public static final int[] OUTGOING_CALL_STATES =
{CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
@@ -4089,11 +4093,14 @@
if (activeCall.isLocallyDisconnecting()) {
callback.onResult(true);
} else {
- Log.i(this, "transactionHoldPotentialActiveCallForNewCallOld: active call could "
- + "not be held or disconnected");
+ String msg = "active call could not be held or disconnected";
+ Log.i(this, "transactionHoldPotentialActiveCallForNewCallOld: " + msg);
callback.onError(
- new CallException("activeCall could not be held or disconnected",
+ new CallException(msg,
CallException.CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL));
+ if (mFeatureFlags.enableCallExceptionAnomReports()) {
+ mAnomalyReporter.reportAnomaly(CANNOT_HOLD_CURRENT_ACTIVE_CALL_ERROR_UUID, msg);
+ }
}
}
}
@@ -4109,19 +4116,25 @@
// early
if (!canHold(activeCall) &&
!(supportsHold(activeCall) && areFromSameSource(activeCall, newCall))) {
- Log.i(this, "transactionHoldPotentialActiveCallForNewCall: "
- + "conditions show the call cannot be held.");
- callback.onError(new CallException("call does not support hold",
+ String msg = "call does not support hold";
+ Log.i(this, "transactionHoldPotentialActiveCallForNewCall: " + msg);
+ callback.onError(new CallException(msg,
CallException.CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL));
+ if (mFeatureFlags.enableCallExceptionAnomReports()) {
+ mAnomalyReporter.reportAnomaly(CANNOT_HOLD_CURRENT_ACTIVE_CALL_ERROR_UUID, msg);
+ }
return;
}
// attempt to hold the active call
if (!holdActiveCallForNewCall(newCall)) {
- Log.i(this, "transactionHoldPotentialActiveCallForNewCall: "
- + "attempted to hold call but failed.");
- callback.onError(new CallException("cannot hold active call failed",
+ String msg = "cannot hold active call failed";
+ Log.i(this, "transactionHoldPotentialActiveCallForNewCall: " + msg);
+ callback.onError(new CallException(msg,
CallException.CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL));
+ if (mFeatureFlags.enableCallExceptionAnomReports()) {
+ mAnomalyReporter.reportAnomaly(CANNOT_HOLD_CURRENT_ACTIVE_CALL_ERROR_UUID, msg);
+ }
return;
}
@@ -6834,8 +6847,12 @@
if (mTargetCallFocus.getState() != mPreviousCallState) {
mTargetCallFocus.setState(mPreviousCallState, "resetting call state");
}
- mCallback.onError(new CallException("failed to switch focus to requested call",
+ String msg = "failed to switch focus to requested call";
+ mCallback.onError(new CallException(msg,
CallException.CODE_CALL_CANNOT_BE_SET_TO_ACTIVE));
+ if (mFeatureFlags.enableCallExceptionAnomReports()) {
+ mAnomalyReporter.reportAnomaly(FAILED_TO_SWITCH_FOCUS_ERROR_UUID, msg);
+ }
return;
}
// at this point, we know the FocusManager is able to update successfully
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index af094b7..b9841ba 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -27,8 +27,6 @@
import static android.Manifest.permission.READ_SMS;
import static android.Manifest.permission.REGISTER_SIM_SUBSCRIPTION;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static android.telecom.CallAttributes.DIRECTION_INCOMING;
-import static android.telecom.CallAttributes.DIRECTION_OUTGOING;
import static android.telecom.CallException.CODE_ERROR_UNKNOWN;
import static android.telecom.TelecomManager.TELECOM_TRANSACTION_SUCCESS;
@@ -52,8 +50,6 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -84,15 +80,12 @@
import com.android.internal.telecom.ICallEventCallback;
import com.android.internal.telecom.ITelecomService;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.telecom.callsequencing.voip.OutgoingCallTransactionSequencing;
import com.android.server.telecom.callsequencing.voip.VoipCallMonitor;
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.callsequencing.voip.IncomingCallTransaction;
-import com.android.server.telecom.callsequencing.voip.OutgoingCallTransaction;
import com.android.server.telecom.callsequencing.TransactionManager;
import com.android.server.telecom.callsequencing.CallTransaction;
import com.android.server.telecom.callsequencing.CallTransactionResult;
@@ -147,6 +140,13 @@
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.";
+ public static final UUID CALL_IS_NULL_OR_ID_MISMATCH_UUID =
+ UUID.fromString("b11f3251-474c-4f90-96d6-a256aebc3c19");
+ public static final String CALL_IS_NULL_OR_ID_MISMATCH_MSG =
+ "call is null or id mismatch";
+ public static final UUID ADD_CALL_ON_ERROR_UUID =
+ UUID.fromString("f8e7d6c5-b4a3-9210-8765-432109abcdef");
+
private static final String TAG = "TelecomServiceImpl";
private static final String TIME_LINE_ARG = "timeline";
private static final int DEFAULT_VIDEO_STATE = -1;
@@ -239,6 +239,11 @@
onAddCallControl(callId, callEventCallback, null,
new CallException(ADD_CALL_ERR_MSG,
CODE_ERROR_UNKNOWN));
+ if (mFeatureFlags.enableCallExceptionAnomReports()) {
+ mAnomalyReporter.reportAnomaly(
+ CALL_IS_NULL_OR_ID_MISMATCH_UUID,
+ CALL_IS_NULL_OR_ID_MISMATCH_MSG);
+ }
return;
}
@@ -268,6 +273,11 @@
public void onError(@NonNull CallException exception) {
Log.d(TAG, "addCall: onError: e=[%s]", exception.toString());
onAddCallControl(callId, callEventCallback, null, exception);
+ if (mFeatureFlags.enableCallExceptionAnomReports()) {
+ mAnomalyReporter.reportAnomaly(
+ ADD_CALL_ON_ERROR_UUID,
+ exception.getMessage());
+ }
}
});
}
@@ -3024,7 +3034,10 @@
});
mTransactionManager = TransactionManager.getInstance();
- mTransactionalServiceRepository = new TransactionalServiceRepository(mFeatureFlags);
+ mTransactionManager.setFeatureFlag(mFeatureFlags);
+ mTransactionManager.setAnomalyReporter(mAnomalyReporter);
+ mTransactionalServiceRepository = new TransactionalServiceRepository(mFeatureFlags,
+ mAnomalyReporter);
mBlockedNumbersManager = mFeatureFlags.telecomMainlineBlockedNumbersManager()
? mContext.getSystemService(BlockedNumbersManager.class)
: null;
diff --git a/src/com/android/server/telecom/TransactionalServiceRepository.java b/src/com/android/server/telecom/TransactionalServiceRepository.java
index 5ae459e..954307a 100644
--- a/src/com/android/server/telecom/TransactionalServiceRepository.java
+++ b/src/com/android/server/telecom/TransactionalServiceRepository.java
@@ -35,9 +35,13 @@
private static final Map<PhoneAccountHandle, TransactionalServiceWrapper> mServiceLookupTable =
new HashMap<>();
private final FeatureFlags mFlags;
+ private final AnomalyReporterAdapter mAnomalyReporter;
- public TransactionalServiceRepository(FeatureFlags flags) {
+ public TransactionalServiceRepository(
+ FeatureFlags flags,
+ AnomalyReporterAdapter anomalyReporter) {
mFlags = flags;
+ mAnomalyReporter = anomalyReporter;
}
public TransactionalServiceWrapper addNewCallForTransactionalServiceWrapper
@@ -50,7 +54,8 @@
Log.d(TAG, "creating a new TSW; handle=[%s]", phoneAccountHandle);
service = new TransactionalServiceWrapper(callEventCallback,
callsManager, phoneAccountHandle, call, this,
- TransactionManager.getInstance(), mFlags.enableCallSequencing());
+ TransactionManager.getInstance(), mFlags.enableCallSequencing(),
+ mFlags, mAnomalyReporter);
} else {
Log.d(TAG, "add a new call to an existing TSW; handle=[%s]", phoneAccountHandle);
service = getTransactionalServiceWrapper(phoneAccountHandle);
diff --git a/src/com/android/server/telecom/TransactionalServiceWrapper.java b/src/com/android/server/telecom/TransactionalServiceWrapper.java
index d63a0bd..cc0d547 100644
--- a/src/com/android/server/telecom/TransactionalServiceWrapper.java
+++ b/src/com/android/server/telecom/TransactionalServiceWrapper.java
@@ -47,9 +47,11 @@
import com.android.server.telecom.callsequencing.TransactionManager;
import com.android.server.telecom.callsequencing.CallTransaction;
import com.android.server.telecom.callsequencing.CallTransactionResult;
+import com.android.server.telecom.flags.FeatureFlags;
import java.util.Locale;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
@@ -92,7 +94,12 @@
private TransactionManager mTransactionManager;
private CallStreamingController mStreamingController;
private final TransactionalCallSequencingAdapter mCallSequencingAdapter;
-
+ private final FeatureFlags mFeatureFlags;
+ private final AnomalyReporterAdapter mAnomalyReporter;
+ public static final UUID CALL_IS_NO_LONGER_BEING_TRACKED_ERROR_UUID =
+ UUID.fromString("8187cd59-97a7-4e9f-a772-638dda4b69bb");
+ public static final String CALL_IS_NO_LONGER_BEING_TRACKED_ERROR_MSG =
+ "A call update was attempted for a call no longer being tracked";
// Each TransactionalServiceWrapper should have their own Binder.DeathRecipient to clean up
// any calls in the event the application crashes or is force stopped.
@@ -108,7 +115,8 @@
public TransactionalServiceWrapper(ICallEventCallback callEventCallback,
CallsManager callsManager, PhoneAccountHandle phoneAccountHandle, Call call,
TransactionalServiceRepository repo, TransactionManager transactionManager,
- boolean isCallSequencingEnabled) {
+ boolean isCallSequencingEnabled, FeatureFlags featureFlags,
+ AnomalyReporterAdapter anomalyReporterAdapter) {
// passed args
mICallEventCallback = callEventCallback;
mCallsManager = callsManager;
@@ -123,6 +131,8 @@
mCallSequencingAdapter = new TransactionalCallSequencingAdapter(mTransactionManager,
mCallsManager, isCallSequencingEnabled);
setDeathRecipient(callEventCallback);
+ mFeatureFlags = featureFlags;
+ mAnomalyReporter = anomalyReporterAdapter;
}
public TransactionManager getTransactionManager() {
@@ -307,6 +317,11 @@
+ " via TelecomManager#addCall", action, callId),
CODE_CALL_IS_NOT_BEING_TRACKED));
callback.send(CODE_CALL_IS_NOT_BEING_TRACKED, exceptionBundle);
+ if (mFeatureFlags.enableCallExceptionAnomReports()) {
+ mAnomalyReporter.reportAnomaly(
+ CALL_IS_NO_LONGER_BEING_TRACKED_ERROR_UUID,
+ CALL_IS_NO_LONGER_BEING_TRACKED_ERROR_MSG);
+ }
}
}
diff --git a/src/com/android/server/telecom/callsequencing/CallSequencingController.java b/src/com/android/server/telecom/callsequencing/CallSequencingController.java
index 67ef358..034f02a 100644
--- a/src/com/android/server/telecom/callsequencing/CallSequencingController.java
+++ b/src/com/android/server/telecom/callsequencing/CallSequencingController.java
@@ -42,7 +42,6 @@
import android.telecom.PhoneAccountHandle;
import android.telephony.AnomalyReporter;
import android.telephony.CarrierConfigManager;
-import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.AnomalyReporterAdapter;
@@ -62,6 +61,7 @@
import com.android.server.telecom.stats.CallFailureCause;
import java.util.Objects;
+import java.util.UUID;
import java.util.concurrent.CompletableFuture;
/**
@@ -81,6 +81,10 @@
private final Context mContext;
private final FeatureFlags mFeatureFlags;
private static String TAG = CallSequencingController.class.getSimpleName();
+ public static final UUID SEQUENCING_CANNOT_HOLD_ACTIVE_CALL_UUID =
+ UUID.fromString("ea094d77-6ea9-4e40-891e-14bff5d485d7");
+ public static final String SEQUENCING_CANNOT_HOLD_ACTIVE_CALL_MSG =
+ "Cannot hold active call";
public CallSequencingController(CallsManager callsManager, Context context,
ClockProxy clockProxy, AnomalyReporterAdapter anomalyReporter,
@@ -217,6 +221,12 @@
callback.onError(
new CallException("activeCall could not be held or disconnected",
CallException.CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL));
+ if (mFeatureFlags.enableCallExceptionAnomReports()) {
+ mAnomalyReporter.reportAnomaly(
+ SEQUENCING_CANNOT_HOLD_ACTIVE_CALL_UUID,
+ SEQUENCING_CANNOT_HOLD_ACTIVE_CALL_MSG
+ );
+ }
}
return CompletableFuture.completedFuture(result);
}, new LoggedHandlerExecutor(mHandler, "CM.mCAA", mCallsManager.getLock()));
diff --git a/src/com/android/server/telecom/callsequencing/TransactionManager.java b/src/com/android/server/telecom/callsequencing/TransactionManager.java
index 2a6431b..98d54da 100644
--- a/src/com/android/server/telecom/callsequencing/TransactionManager.java
+++ b/src/com/android/server/telecom/callsequencing/TransactionManager.java
@@ -25,6 +25,8 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.telecom.AnomalyReporterAdapter;
+import com.android.server.telecom.flags.FeatureFlags;
import com.android.server.telecom.flags.Flags;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -32,6 +34,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Queue;
+import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class TransactionManager {
@@ -43,6 +46,12 @@
private final Deque<CallTransaction> mCompletedTransactions;
private CallTransaction mCurrentTransaction;
private boolean mProcessingCallSequencing;
+ private AnomalyReporterAdapter mAnomalyReporter;
+ private FeatureFlags mFeatureFlags;
+ public static final UUID TRANSACTION_MANAGER_TIMEOUT_UUID =
+ UUID.fromString("9ccce52e-6694-4357-9e5e-516a9531b062");
+ public static final String TRANSACTION_MANAGER_TIMEOUT_MSG =
+ "TransactionManager hit a timeout while processing a transaction";
public interface TransactionCompleteListener {
void onTransactionCompleted(CallTransactionResult result, String transactionName);
@@ -67,6 +76,14 @@
return INSTANCE;
}
+ public void setFeatureFlag(FeatureFlags flag){
+ mFeatureFlags = flag;
+ }
+
+ public void setAnomalyReporter(AnomalyReporterAdapter callAnomalyReporter){
+ mAnomalyReporter = callAnomalyReporter;
+ }
+
@VisibleForTesting
public static TransactionManager getTestInstance() {
return new TransactionManager();
@@ -109,6 +126,12 @@
receiver.onError(new CallException(transactionName + " timeout",
CODE_OPERATION_TIMED_OUT));
transactionCompleteFuture.complete(false);
+ if (mFeatureFlags != null && mAnomalyReporter != null &&
+ mFeatureFlags.enableCallExceptionAnomReports()) {
+ mAnomalyReporter.reportAnomaly(
+ TRANSACTION_MANAGER_TIMEOUT_UUID,
+ TRANSACTION_MANAGER_TIMEOUT_MSG);
+ }
} catch (Exception e) {
Log.e(TAG, String.format("onTransactionTimeout: Notifying transaction "
+ " %s resulted in an Exception.", transactionName), e);
diff --git a/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java b/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
index fea6135..16b6e44 100644
--- a/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
+++ b/tests/src/com/android/server/telecom/tests/TransactionalServiceWrapperTest.java
@@ -34,6 +34,7 @@
import com.android.internal.telecom.ICallControl;
import com.android.internal.telecom.ICallEventCallback;
+import com.android.server.telecom.AnomalyReporterAdapter;
import com.android.server.telecom.Call;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.TelecomSystem;
@@ -70,6 +71,7 @@
@Mock private TransactionManager mTransactionManager;
@Mock private ICallEventCallback mCallEventCallback;
@Mock private TransactionalServiceRepository mRepository;
+ @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
@Mock private IBinder mIBinder;
private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {};
@@ -84,7 +86,7 @@
Mockito.when(mCallEventCallback.asBinder()).thenReturn(mIBinder);
mTransactionalServiceWrapper = new TransactionalServiceWrapper(mCallEventCallback,
mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository, mTransactionManager,
- false /*call sequencing*/);
+ false /*call sequencing*/, mFeatureFlags, mAnomalyReporterAdapter);
}
@Override
@@ -98,7 +100,7 @@
TransactionalServiceWrapper service =
new TransactionalServiceWrapper(mCallEventCallback,
mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository, mTransactionManager,
- false /*call sequencing*/);
+ false /*call sequencing*/, mFeatureFlags, mAnomalyReporterAdapter);
assertEquals(SERVICE_HANDLE, service.getPhoneAccountHandle());
assertEquals(1, service.getNumberOfTrackedCalls());
@@ -109,7 +111,7 @@
TransactionalServiceWrapper service =
new TransactionalServiceWrapper(mCallEventCallback,
mCallsManager, SERVICE_HANDLE, mMockCall1, mRepository, mTransactionManager,
- false /*call sequencing*/);
+ false /*call sequencing*/, mFeatureFlags, mAnomalyReporterAdapter);
assertEquals(1, service.getNumberOfTrackedCalls());
service.trackCall(mMockCall2);