Merge "Use IncomingCallFilterGraph to perform incoming call filters." am: 482556f24a am: 40dba6c807
am: 464e671f7b

Change-Id: If2e4afdce1c3c54788a8dc3caac2a6b0f96cd527
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 31396db..230e890 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -20,6 +20,7 @@
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -34,6 +35,7 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -74,14 +76,15 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
-import com.android.server.telecom.callfiltering.AsyncBlockCheckFilter;
 import com.android.server.telecom.callfiltering.BlockCheckerAdapter;
+import com.android.server.telecom.callfiltering.BlockCheckerFilter;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
 import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
-import com.android.server.telecom.callfiltering.CallScreeningServiceController;
-import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter;
+import com.android.server.telecom.callfiltering.DirectToVoicemailFilter;
 import com.android.server.telecom.callfiltering.IncomingCallFilter;
+import com.android.server.telecom.callfiltering.IncomingCallFilterGraph;
+import com.android.server.telecom.callfiltering.NewCallScreeningServiceFilter;
 import com.android.server.telecom.callredirection.CallRedirectionProcessor;
 import com.android.server.telecom.components.ErrorDialogActivity;
 import com.android.server.telecom.settings.BlockedNumbersUtil;
@@ -91,13 +94,13 @@
 import com.android.server.telecom.ui.ConfirmCallDialogActivity;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -357,6 +360,8 @@
 
     private Runnable mStopTone;
 
+    private LinkedList<HandlerThread> mGraphHandlerThreads;
+
     /**
      * Listener to PhoneAccountRegistrar events.
      */
@@ -547,6 +552,7 @@
                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         intentFilter.addAction(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED);
         context.registerReceiver(mReceiver, intentFilter);
+        mGraphHandlerThreads = new LinkedList<>();
     }
 
     public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) {
@@ -630,14 +636,16 @@
             return;
         }
 
+        IncomingCallFilterGraph graph = setUpCallFilterGraph(incomingCall);
+        graph.performFiltering();
+    }
+
+    private IncomingCallFilterGraph setUpCallFilterGraph(Call incomingCall) {
         incomingCall.setIsUsingCallFiltering(true);
-        List<IncomingCallFilter.CallFilter> filters = new ArrayList<>();
-        filters.add(new DirectToVoicemailCallFilter(mCallerInfoLookupHelper));
-        filters.add(new AsyncBlockCheckFilter(mContext, new BlockCheckerAdapter(),
-                mCallerInfoLookupHelper, null));
-        filters.add(new CallScreeningServiceController(mContext, this, mPhoneAccountRegistrar,
-                new ParcelableCallUtils.Converter(), mLock,
-                new TelecomServiceImpl.SettingsSecureAdapterImpl(), mCallerInfoLookupHelper,
+        String carrierPackageName = getCarrierPackageName();
+        String defaultDialerPackageName = TelecomManager.from(mContext).getDefaultDialerPackage();
+        String userChosenPackageName = getRoleManagerAdapter().getDefaultCallScreeningApp();
+        CallScreeningServiceHelper.AppLabelProxy appLabelProxy =
                 new CallScreeningServiceHelper.AppLabelProxy() {
                     @Override
                     public CharSequence getAppLabel(String packageName) {
@@ -651,9 +659,53 @@
 
                         return null;
                     }
-                }));
-        mIncomingCallFilterFactory.create(mContext, this, incomingCall, mLock,
-                mTimeoutsAdapter, filters).performFiltering();
+                };
+        ParcelableCallUtils.Converter converter = new ParcelableCallUtils.Converter();
+
+        IncomingCallFilterGraph graph = new IncomingCallFilterGraph(incomingCall,
+                this::onCallFilteringComplete, mContext, mTimeoutsAdapter, mLock);
+        DirectToVoicemailFilter voicemailFilter = new DirectToVoicemailFilter(incomingCall,
+                mCallerInfoLookupHelper);
+        BlockCheckerFilter blockCheckerFilter = new BlockCheckerFilter(mContext, incomingCall,
+                mCallerInfoLookupHelper, new BlockCheckerAdapter());
+        NewCallScreeningServiceFilter carrierCallScreeningServiceFilter =
+                new NewCallScreeningServiceFilter(incomingCall, carrierPackageName,
+                        NewCallScreeningServiceFilter.PACKAGE_TYPE_CARRIER, mContext, this,
+                        appLabelProxy, converter);
+        NewCallScreeningServiceFilter defaultDialerCallScreeningServiceFilter =
+                new NewCallScreeningServiceFilter(incomingCall, defaultDialerPackageName,
+                        NewCallScreeningServiceFilter.PACKAGE_TYPE_DEFAULT_DIALER, mContext, this,
+                        appLabelProxy, converter);
+        NewCallScreeningServiceFilter userChosenCallScreeningServiceFilter =
+                new NewCallScreeningServiceFilter(incomingCall, userChosenPackageName,
+                        NewCallScreeningServiceFilter.PACKAGE_TYPE_USER_CHOSEN, mContext, this,
+                        appLabelProxy, converter);
+        graph.addFilter(voicemailFilter);
+        graph.addFilter(blockCheckerFilter);
+        graph.addFilter(carrierCallScreeningServiceFilter);
+        graph.addFilter(defaultDialerCallScreeningServiceFilter);
+        graph.addFilter(userChosenCallScreeningServiceFilter);
+        IncomingCallFilterGraph.addEdge(voicemailFilter, carrierCallScreeningServiceFilter);
+        IncomingCallFilterGraph.addEdge(blockCheckerFilter, carrierCallScreeningServiceFilter);
+        IncomingCallFilterGraph.addEdge(carrierCallScreeningServiceFilter,
+                defaultDialerCallScreeningServiceFilter);
+        IncomingCallFilterGraph.addEdge(carrierCallScreeningServiceFilter,
+                userChosenCallScreeningServiceFilter);
+        mGraphHandlerThreads.add(graph.getHandlerThread());
+        return graph;
+    }
+
+    private String getCarrierPackageName() {
+        ComponentName componentName = null;
+        CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService
+                (Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle configBundle = configManager.getConfig();
+        if (configBundle != null) {
+            componentName = ComponentName.unflattenFromString(configBundle.getString
+                    (CarrierConfigManager.KEY_CARRIER_CALL_SCREENING_APP_STRING, ""));
+        }
+
+        return componentName != null ? componentName.getPackageName() : null;
     }
 
     @Override
@@ -661,6 +713,8 @@
         // Only set the incoming call as ringing if it isn't already disconnected. It is possible
         // that the connection service disconnected the call before it was even added to Telecom, in
         // which case it makes no sense to set it back to a ringing state.
+        mGraphHandlerThreads.clear();
+
         if (incomingCall.getState() != CallState.DISCONNECTED &&
                 incomingCall.getState() != CallState.DISCONNECTING) {
             setCallState(incomingCall, CallState.RINGING,
@@ -4768,4 +4822,8 @@
                 .filter(c -> phoneAccount.getAccountHandle().equals(c.getTargetPhoneAccount()))
                 .forEach(c -> c.setVideoCallingSupportedByPhoneAccount(isVideoNowSupported));
     }
+
+    public LinkedList<HandlerThread> getGraphHandlerThreads() {
+        return mGraphHandlerThreads;
+    }
 }
diff --git a/src/com/android/server/telecom/callfiltering/CallFilteringResult.java b/src/com/android/server/telecom/callfiltering/CallFilteringResult.java
index 3f64f2c..44d5708 100644
--- a/src/com/android/server/telecom/callfiltering/CallFilteringResult.java
+++ b/src/com/android/server/telecom/callfiltering/CallFilteringResult.java
@@ -182,6 +182,7 @@
                 .setShouldAddToCallLog(shouldAddToCallLog && other.shouldAddToCallLog)
                 .setShouldShowNotification(shouldShowNotification && other.shouldShowNotification)
                 .setShouldScreenViaAudio(shouldScreenViaAudio || other.shouldScreenViaAudio)
+                .setContactExists(contactExists || other.contactExists)
                 .build();
     }
 
diff --git a/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java b/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java
index b39854c..03d27bd 100644
--- a/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java
+++ b/src/com/android/server/telecom/callfiltering/IncomingCallFilterGraph.java
@@ -24,6 +24,7 @@
 
 import com.android.server.telecom.Call;
 import com.android.server.telecom.LoggedHandlerExecutor;
+import com.android.server.telecom.LogUtils;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.Timeouts;
 
@@ -62,6 +63,7 @@
         }
 
         public CallFilteringResult whenDone(CallFilteringResult result) {
+            Log.i(TAG, "Filter %s done, result: %s.", mFilter, result);
             mFilter.result = result;
             for (CallFilter filter : mFilter.getFollowings()) {
                 if (filter.decrementAndGetIndegree() == 0) {
@@ -72,6 +74,7 @@
                 synchronized (mLock) {
                     mFinished = true;
                     mListener.onCallFilteringComplete(mCall, result);
+                    Log.addEvent(mCall, LogUtils.Events.FILTERING_COMPLETED, result);
                 }
                 mHandlerThread.quit();
             }
@@ -100,7 +103,7 @@
     }
 
     public void performFiltering() {
-
+        Log.addEvent(mCall, LogUtils.Events.FILTERING_INITIATED);
         CallFilter dummyStart = new CallFilter();
         mDummyComplete = new CallFilter();
 
@@ -118,6 +121,7 @@
             public void loggedRun() {
                 if (!mFinished) {
                     Log.i(this, "Graph timed out when performing filtering.");
+                    Log.addEvent(mCall, LogUtils.Events.FILTERING_TIMED_OUT);
                     mListener.onCallFilteringComplete(mCall, mCurrentResult);
                     mFinished = true;
                     mHandlerThread.quit();
@@ -148,10 +152,15 @@
                 new LoggedHandlerExecutor(mHandler, "ICFG.sF", null))
                 .thenApplyAsync(postFilterTask::whenDone,
                         new LoggedHandlerExecutor(mHandler, "ICFG.sF", null));
+        Log.i(TAG, "Filter %s scheduled.", filter);
     }
 
     public static void addEdge(CallFilter before, CallFilter after) {
         before.addFollowings(after);
         after.addDependency(before);
     }
+
+    public HandlerThread getHandlerThread() {
+        return mHandlerThread;
+    }
 }
diff --git a/src/com/android/server/telecom/callfiltering/NewCallScreeningServiceFilter.java b/src/com/android/server/telecom/callfiltering/NewCallScreeningServiceFilter.java
index aa81a49..80996e5 100644
--- a/src/com/android/server/telecom/callfiltering/NewCallScreeningServiceFilter.java
+++ b/src/com/android/server/telecom/callfiltering/NewCallScreeningServiceFilter.java
@@ -16,8 +16,6 @@
 
 package com.android.server.telecom.callfiltering;
 
-import static com.android.server.telecom.callfiltering.IncomingCallFilterGraph.DEFAULT_RESULT;
-
 import android.Manifest;
 import android.content.ComponentName;
 import android.content.Context;
@@ -70,7 +68,7 @@
             if (mCall == null || (!mCall.getId().equals(callId))) {
                 Log.w(this, "allowCall, unknown call id: %s", callId);
             }
-            mResultFuture.complete(DEFAULT_RESULT);
+            mResultFuture.complete(mPriorStageResult);
             Binder.restoreCallingIdentity(token);
             unbindCallScreeningService();
         }
@@ -91,10 +89,11 @@
                         .setCallBlockReason(CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
                         .setCallScreeningAppName(mAppName)
                         .setCallScreeningComponentName(componentName.flattenToString())
+                        .setContactExists(mPriorStageResult.contactExists)
                         .build());
             } else {
                 Log.w(this, "disallowCall, unknown call id: %s", callId);
-                mResultFuture.complete(DEFAULT_RESULT);
+                mResultFuture.complete(mPriorStageResult);
             }
             Binder.restoreCallingIdentity(token);
             unbindCallScreeningService();
@@ -110,10 +109,11 @@
                         .setShouldSilence(true)
                         .setShouldAddToCallLog(true)
                         .setShouldShowNotification(true)
+                        .setContactExists(mPriorStageResult.contactExists)
                         .build());
             } else {
                 Log.w(this, "silenceCall, unknown call id: %s" , callId);
-                mResultFuture.complete(DEFAULT_RESULT);
+                mResultFuture.complete(mPriorStageResult);
             }
             Binder.restoreCallingIdentity(token);
             unbindCallScreeningService();
@@ -134,10 +134,11 @@
                         .setShouldReject(false)
                         .setShouldSilence(false)
                         .setShouldScreenViaAudio(true)
+                        .setContactExists(mPriorStageResult.contactExists)
                         .build());
             } else {
                 Log.w(this, "screenCallFurther, unknown call id: %s", callId);
-                mResultFuture.complete(DEFAULT_RESULT);
+                mResultFuture.complete(mPriorStageResult);
             }
             Binder.restoreCallingIdentity(token);
             unbindCallScreeningService();
@@ -162,26 +163,26 @@
                                 toParcelableCallForScreening(mCall, isSystemDialer()));
             } catch (RemoteException e) {
                 Log.e(this, e, "Failed to set the call screening adapter");
-                mResultFuture.complete(DEFAULT_RESULT);
+                mResultFuture.complete(mPriorStageResult);
             }
             Log.i(this, "Binding completed.");
         }
 
         @Override
         public void onServiceDisconnected(ComponentName componentName) {
-            mResultFuture.complete(DEFAULT_RESULT);
+            mResultFuture.complete(mPriorStageResult);
             Log.i(this, "Service disconnected.");
         }
 
         @Override
         public void onBindingDied(ComponentName name) {
-            mResultFuture.complete(DEFAULT_RESULT);
+            mResultFuture.complete(mPriorStageResult);
             Log.i(this, "Binding died.");
         }
 
         @Override
         public void onNullBinding(ComponentName name) {
-            mResultFuture.complete(DEFAULT_RESULT);
+            mResultFuture.complete(mPriorStageResult);
             Log.i(this, "Null binding.");
         }
     }
@@ -206,20 +207,22 @@
     }
 
     @Override
-    public CompletionStage<CallFilteringResult> startFilterLookup(CallFilteringResult priorStageResult) {
+    public CompletionStage<CallFilteringResult> startFilterLookup(
+            CallFilteringResult priorStageResult) {
+        mPriorStageResult = priorStageResult;
         if (mPackageName == null) {
-            return CompletableFuture.completedFuture(DEFAULT_RESULT);
+            return CompletableFuture.completedFuture(priorStageResult);
         }
 
         if (!priorStageResult.shouldAllowCall) {
             // Call already blocked by other filters, no need to bind to call screening service.
-            return CompletableFuture.completedFuture(DEFAULT_RESULT);
+            return CompletableFuture.completedFuture(priorStageResult);
         }
 
         if (priorStageResult.contactExists && (!hasReadContactsPermission())) {
             // Binding to the call screening service will be skipped if it does NOT hold
             // READ_CONTACTS permission and the number is in the user’s contacts
-            return CompletableFuture.completedFuture(DEFAULT_RESULT);
+            return CompletableFuture.completedFuture(priorStageResult);
         }
 
         CompletableFuture<CallFilteringResult> resultFuture = new CompletableFuture<>();
@@ -245,7 +248,7 @@
         if (!CallScreeningServiceHelper.bindCallScreeningService(mContext,
                 mCallsManager.getCurrentUserHandle(), mPackageName, mConnection)) {
             Log.i(this, "Call screening service binding failed.");
-            resultFuture.complete(DEFAULT_RESULT);
+            resultFuture.complete(mPriorStageResult);
         }
     }
 
@@ -265,7 +268,6 @@
     }
 
     private boolean packageTypeShouldAdd(int packageType) {
-        return packageType == PACKAGE_TYPE_CARRIER;
+        return packageType != PACKAGE_TYPE_CARRIER;
     }
-
 }
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 4fc9ed6..cfc530d 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -21,14 +21,12 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.isNotNull;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -78,14 +76,12 @@
 import com.android.server.telecom.ClockProxy;
 import com.android.server.telecom.ConnectionServiceFocusManager;
 import com.android.server.telecom.ContactsAsyncHelper;
-import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
 import com.android.server.telecom.InCallWakeLockController;
 import com.android.server.telecom.InCallWakeLockControllerFactory;
 import com.android.server.telecom.MissedCallNotifier;
 import com.android.server.telecom.PhoneAccountRegistrar;
-import com.android.server.telecom.PhoneNumberUtilsAdapter;
 import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
 import com.android.server.telecom.ProximitySensorManager;
 import com.android.server.telecom.ProximitySensorManagerFactory;
@@ -111,6 +107,7 @@
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -377,6 +374,12 @@
     @Override
     public void tearDown() throws Exception {
         mTelecomSystem.getCallsManager().waitOnHandlers();
+        LinkedList<HandlerThread> handlerThreads = mTelecomSystem.getCallsManager()
+                .getGraphHandlerThreads();
+        for (HandlerThread handlerThread : handlerThreads) {
+            handlerThread.quitSafely();
+        }
+        handlerThreads.clear();
         waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
         waitForHandlerAction(mHandlerThread.getThreadHandler(), TEST_TIMEOUT);
         // Bring down the threads that are active.