Merge "Resolve baseline call audio routing" into main
diff --git a/flags/telecom_call_filtering_flags.aconfig b/flags/telecom_call_filtering_flags.aconfig
index d80cfa3..693d727 100644
--- a/flags/telecom_call_filtering_flags.aconfig
+++ b/flags/telecom_call_filtering_flags.aconfig
@@ -7,4 +7,15 @@
namespace: "telecom"
description: "Gates whether to still perform Dnd filter when phone account has skip_filter call extra."
bug: "222333869"
-}
\ No newline at end of file
+}
+
+# OWNER=tjstuart TARGET=25Q1
+flag {
+ name: "check_completed_filters_on_timeout"
+ namespace: "telecom"
+ description: "If the Filtering Graph times out, combine the finished results"
+ bug: "364946812"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 1e6d2bc..04f93fc 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -913,7 +913,7 @@
DndCallFilter dndCallFilter = new DndCallFilter(incomingHfpCall, mRinger);
IncomingCallFilterGraph graph = mIncomingCallFilterGraphProvider.createGraph(
incomingHfpCall,
- this::onCallFilteringComplete, mContext, mTimeoutsAdapter, mLock);
+ this::onCallFilteringComplete, mContext, mTimeoutsAdapter, mFeatureFlags, mLock);
graph.addFilter(dndCallFilter);
mGraphHandlerThreads.add(graph.getHandlerThread());
return graph;
@@ -932,7 +932,7 @@
ParcelableCallUtils.Converter converter = new ParcelableCallUtils.Converter();
IncomingCallFilterGraph graph = mIncomingCallFilterGraphProvider.createGraph(incomingCall,
- this::onCallFilteringComplete, mContext, mTimeoutsAdapter, mLock);
+ this::onCallFilteringComplete, mContext, mTimeoutsAdapter, mFeatureFlags, mLock);
DirectToVoicemailFilter voicemailFilter = new DirectToVoicemailFilter(incomingCall,
mCallerInfoLookupHelper);
BlockCheckerFilter blockCheckerFilter = new BlockCheckerFilter(mContext, incomingCall,
diff --git a/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java b/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java
index d79e80e..a606a4d 100644
--- a/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java
+++ b/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java
@@ -27,6 +27,7 @@
import com.android.server.telecom.LogUtils;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.Timeouts;
+import com.android.server.telecom.flags.FeatureFlags;
import java.util.ArrayList;
import java.util.List;
@@ -55,6 +56,7 @@
private CallFilteringResult mCurrentResult;
private Context mContext;
private Timeouts.Adapter mTimeoutsAdapter;
+ private final FeatureFlags mFeatureFlags;
private class PostFilterTask {
private final CallFilter mFilter;
@@ -84,11 +86,12 @@
}
public IncomingCallFilterGraph(Call call, CallFilterResultCallback listener, Context context,
- Timeouts.Adapter timeoutsAdapter, TelecomSystem.SyncRoot lock) {
+ Timeouts.Adapter timeoutsAdapter, FeatureFlags featureFlags,
+ TelecomSystem.SyncRoot lock) {
mListener = listener;
mCall = call;
mFiltersList = new ArrayList<>();
-
+ mFeatureFlags = featureFlags;
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
@@ -121,8 +124,8 @@
@Override
public void loggedRun() {
if (!mFinished) {
- Log.i(this, "Graph timed out when performing filtering.");
Log.addEvent(mCall, LogUtils.Events.FILTERING_TIMED_OUT);
+ mCurrentResult = onTimeoutCombineFinishedFilters(mFiltersList, mCurrentResult);
mListener.onCallFilteringComplete(mCall, mCurrentResult, true);
mFinished = true;
mHandlerThread.quit();
@@ -137,6 +140,28 @@
}.prepare(), mTimeoutsAdapter.getCallScreeningTimeoutMillis(mContext.getContentResolver()));
}
+ /**
+ * This helper takes all the call filters that were added to the graph, checks if filters have
+ * finished, and combines the results.
+ *
+ * @param filtersList all the CallFilters that were added to the call
+ * @param currentResult the current call filter result
+ * @return CallFilterResult of the combined finished Filters.
+ */
+ private CallFilteringResult onTimeoutCombineFinishedFilters(
+ List<CallFilter> filtersList,
+ CallFilteringResult currentResult) {
+ if (!mFeatureFlags.checkCompletedFiltersOnTimeout()) {
+ return currentResult;
+ }
+ for (CallFilter filter : filtersList) {
+ if (filter.result != null) {
+ currentResult = currentResult.combine(filter.result);
+ }
+ }
+ return currentResult;
+ }
+
private void scheduleFilter(CallFilter filter) {
CallFilteringResult result = new CallFilteringResult.Builder()
.setShouldAllowCall(true)
@@ -147,6 +172,9 @@
.setDndSuppressed(false)
.build();
for (CallFilter dependencyFilter : filter.getDependencies()) {
+ // When sequential nodes are completed, they are combined progressively.
+ // ex.) node_a --> node_b --> node_c
+ // node_a will combine with node_b before starting node_c
result = result.combine(dependencyFilter.getResult());
}
mCurrentResult = result;
diff --git a/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraphProvider.java b/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraphProvider.java
index 1501280..4424178 100644
--- a/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraphProvider.java
+++ b/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraphProvider.java
@@ -21,6 +21,7 @@
import com.android.server.telecom.Call;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.Timeouts;
+import com.android.server.telecom.flags.FeatureFlags;
/**
* Interface to provide a {@link IncomingCallFilterGraph}. This class serve for unit test purpose
@@ -35,10 +36,13 @@
* @param listener Callback object to trigger when filtering is done.
* @param context An android context.
* @param timeoutsAdapter Adapter to provide timeout value for call filtering.
+ * @param featureFlags Telecom flags
* @param lock Telecom lock.
* @return
*/
IncomingCallFilterGraph createGraph(Call call, CallFilterResultCallback listener,
Context context,
- Timeouts.Adapter timeoutsAdapter, TelecomSystem.SyncRoot lock);
+ Timeouts.Adapter timeoutsAdapter,
+ FeatureFlags featureFlags,
+ TelecomSystem.SyncRoot lock);
}
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index baf0208..74f33c3 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -392,7 +392,8 @@
mBluetoothDeviceManager,
mFeatureFlags,
mTelephonyFlags,
- (call, listener, context, timeoutsAdapter, lock) -> mIncomingCallFilterGraph);
+ (call, listener, context, timeoutsAdapter,
+ mFeatureFlags, lock) -> mIncomingCallFilterGraph);
when(mPhoneAccountRegistrar.getPhoneAccount(
eq(SELF_MANAGED_HANDLE), any())).thenReturn(SELF_MANAGED_ACCOUNT);
diff --git a/tests/src/com/android/server/telecom/tests/IncomingCallFilterGraphTest.java b/tests/src/com/android/server/telecom/tests/IncomingCallFilterGraphTest.java
index 66ac553..d7905b2 100644
--- a/tests/src/com/android/server/telecom/tests/IncomingCallFilterGraphTest.java
+++ b/tests/src/com/android/server/telecom/tests/IncomingCallFilterGraphTest.java
@@ -17,22 +17,28 @@
package com.android.server.telecom.tests;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
+import android.util.Log;
import androidx.test.filters.SmallTest;
import com.android.server.telecom.Call;
+import com.android.server.telecom.Ringer;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.Timeouts;
import com.android.server.telecom.callfiltering.CallFilter;
import com.android.server.telecom.callfiltering.CallFilterResultCallback;
import com.android.server.telecom.callfiltering.CallFilteringResult;
+import com.android.server.telecom.callfiltering.DndCallFilter;
import com.android.server.telecom.callfiltering.IncomingCallFilterGraph;
import org.junit.Before;
@@ -47,6 +53,7 @@
@RunWith(JUnit4.class)
public class IncomingCallFilterGraphTest extends TelecomTestCase {
+ private final String TAG = IncomingCallFilterGraphTest.class.getSimpleName();
@Mock private Call mCall;
@Mock private Context mContext;
@Mock private Timeouts.Adapter mTimeoutsAdapter;
@@ -88,13 +95,15 @@
@Override
public CompletionStage<CallFilteringResult> startFilterLookup(
CallFilteringResult priorStageResult) {
- HandlerThread handlerThread = new HandlerThread("TimeoutFilter");
- handlerThread.start();
- Handler handler = new Handler(handlerThread.getLooper());
-
- CompletableFuture<CallFilteringResult> resultFuture = new CompletableFuture<>();
- handler.postDelayed(() -> resultFuture.complete(PASS_CALL_RESULT),
- TIMEOUT_FILTER_SLEEP_TIME);
+ Log.i(TAG, "TimeoutFilter: startFilterLookup: about to sleep");
+ try {
+ // Currently, there are no tools to fake a timeout with [CompletableFuture]s
+ // in the Android Platform. Thread sleep is the best option for an end-to-end test.
+ Thread.sleep(FILTER_TIMEOUT); // Simulate a filter timeout
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ Log.i(TAG, "TimeoutFilter: startFilterLookup: continuing test");
return CompletableFuture.completedFuture(PASS_CALL_RESULT);
}
}
@@ -116,7 +125,7 @@
CallFilterResultCallback listener = (call, result, timeout) -> testResult.complete(result);
IncomingCallFilterGraph graph = new IncomingCallFilterGraph(mCall, listener, mContext,
- mTimeoutsAdapter, mLock);
+ mTimeoutsAdapter, mFeatureFlags, mLock);
graph.performFiltering();
assertEquals(PASS_CALL_RESULT, testResult.get(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
@@ -129,7 +138,7 @@
CallFilterResultCallback listener = (call, result, timeout) -> testResult.complete(result);
IncomingCallFilterGraph graph = new IncomingCallFilterGraph(mCall, listener, mContext,
- mTimeoutsAdapter, mLock);
+ mTimeoutsAdapter, mFeatureFlags, mLock);
AllowFilter allowFilter = new AllowFilter();
DisallowFilter disallowFilter = new DisallowFilter();
graph.addFilter(allowFilter);
@@ -147,7 +156,7 @@
CallFilterResultCallback listener = (call, result, timeout) -> testResult.complete(result);
IncomingCallFilterGraph graph = new IncomingCallFilterGraph(mCall, listener, mContext,
- mTimeoutsAdapter, mLock);
+ mTimeoutsAdapter, mFeatureFlags, mLock);
AllowFilter allowFilter1 = new AllowFilter();
AllowFilter allowFilter2 = new AllowFilter();
DisallowFilter disallowFilter = new DisallowFilter();
@@ -166,7 +175,7 @@
CallFilterResultCallback listener = (call, result, timeout) -> testResult.complete(result);
IncomingCallFilterGraph graph = new IncomingCallFilterGraph(mCall, listener, mContext,
- mTimeoutsAdapter, mLock);
+ mTimeoutsAdapter, mFeatureFlags, mLock);
DisallowFilter disallowFilter = new DisallowFilter();
TimeoutFilter timeoutFilter = new TimeoutFilter();
graph.addFilter(disallowFilter);
@@ -176,4 +185,57 @@
assertEquals(REJECT_CALL_RESULT, testResult.get(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
}
+
+ /**
+ * Verify that when the Call Filtering Graph times out, already completed filters are combined.
+ * Graph being tested:
+ *
+ * startFilterLookup --> [ ALLOW_FILTER ]
+ * |
+ * ---------------------------------
+ * | |
+ * | |
+ * [DND_FILTER] [TIMEOUT_FILTER]
+ * | |
+ * | * timeout at 5 seconds *
+ * |
+ * |
+ * --------[ CallFilteringResult ]
+ */
+ @SmallTest
+ @Test
+ public void testFilterTimesOutWithDndFilterComputedAlready() throws Exception {
+ // GIVEN: a graph that is set up like the above diagram in the test comment
+ Ringer mockRinger = mock(Ringer.class);
+ CompletableFuture<CallFilteringResult> testResult = new CompletableFuture<>();
+ IncomingCallFilterGraph graph = new IncomingCallFilterGraph(
+ mCall,
+ (call, result, timeout) -> testResult.complete(result),
+ mContext,
+ mTimeoutsAdapter,
+ mFeatureFlags,
+ mLock);
+ // create the filters / nodes for the graph
+ TimeoutFilter timeoutFilter = new TimeoutFilter();
+ DndCallFilter dndCallFilter = new DndCallFilter(mCall, mockRinger);
+ AllowFilter allowFilter1 = new AllowFilter();
+ // adding them to the graph does not create the edges
+ graph.addFilter(allowFilter1);
+ graph.addFilter(timeoutFilter);
+ graph.addFilter(dndCallFilter);
+ // set up the graph so that the DND filter can process in parallel to the timeout
+ IncomingCallFilterGraph.addEdge(allowFilter1, dndCallFilter);
+ IncomingCallFilterGraph.addEdge(allowFilter1, timeoutFilter);
+
+ // WHEN: DND is on and the caller cannot interrupt and the graph is processed
+ when(mockRinger.shouldRingForContact(mCall)).thenReturn(false);
+ when(mFeatureFlags.checkCompletedFiltersOnTimeout()).thenReturn(true);
+ dndCallFilter.startFilterLookup(IncomingCallFilterGraph.DEFAULT_RESULT);
+ graph.performFiltering();
+
+ // THEN: assert shouldSuppressCallDueToDndStatus is true!
+ assertFalse(IncomingCallFilterGraph.DEFAULT_RESULT.shouldSuppressCallDueToDndStatus);
+ assertTrue(testResult.get(TIMEOUT_FILTER_SLEEP_TIME,
+ TimeUnit.MILLISECONDS).shouldSuppressCallDueToDndStatus);
+ }
}