Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed0cef05 authored by Grace Jia's avatar Grace Jia
Browse files

Refactored CallScreeningServiceFilter to NewCallScreeningServiceFilter.

moved checking contact exist part to BlockCheckerFiltersince it
need to query caller info and will always perform filtering
before call screen service binding.

Test: TelecomUnitTest
Bug: 135929421
Change-Id: I52c96f2d760446d3444825ec2ad78e6ddc9226c6
parent 503bc647
Loading
Loading
Loading
Loading
+31 −3
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ public class CallFilteringResult {
        private boolean mShouldShowNotification;
        private boolean mShouldSilence = false;
        private boolean mShouldScreenViaAudio = false;
        private boolean mContactExists = false;
        private int mCallBlockReason = Calls.BLOCK_REASON_NOT_BLOCKED;
        private CharSequence mCallScreeningAppName = null;
        private String mCallScreeningComponentName = null;
@@ -77,11 +78,30 @@ public class CallFilteringResult {
            return this;
        }

        public Builder setContactExists(boolean contactExists) {
            mContactExists = contactExists;
            return this;
        }

        public static Builder from(CallFilteringResult result) {
            return new Builder()
                    .setShouldAllowCall(result.shouldAllowCall)
                    .setShouldReject(result.shouldReject)
                    .setShouldAddToCallLog(result.shouldAddToCallLog)
                    .setShouldShowNotification(result.shouldShowNotification)
                    .setShouldSilence(result.shouldSilence)
                    .setCallBlockReason(result.mCallBlockReason)
                    .setShouldScreenViaAudio(result.shouldScreenViaAudio)
                    .setCallScreeningAppName(result.mCallScreeningAppName)
                    .setCallScreeningComponentName(result.mCallScreeningComponentName)
                    .setContactExists(result.contactExists);
        }

        public CallFilteringResult build() {
            return new CallFilteringResult(mShouldAllowCall, mShouldReject, mShouldSilence,
                    mShouldAddToCallLog, mShouldShowNotification, mCallBlockReason,
                    mCallScreeningAppName, mCallScreeningComponentName,
                    mShouldScreenViaAudio);
                    mCallScreeningAppName, mCallScreeningComponentName, mShouldScreenViaAudio,
                    mContactExists);
        }
    }

@@ -94,11 +114,12 @@ public class CallFilteringResult {
    public int mCallBlockReason;
    public CharSequence mCallScreeningAppName;
    public String mCallScreeningComponentName;
    public boolean contactExists;

    private CallFilteringResult(boolean shouldAllowCall, boolean shouldReject, boolean
            shouldSilence, boolean shouldAddToCallLog, boolean shouldShowNotification, int
            callBlockReason, CharSequence callScreeningAppName, String callScreeningComponentName,
            boolean shouldScreenViaAudio) {
            boolean shouldScreenViaAudio, boolean contactExists) {
        this.shouldAllowCall = shouldAllowCall;
        this.shouldReject = shouldReject;
        this.shouldSilence = shouldSilence;
@@ -108,6 +129,7 @@ public class CallFilteringResult {
        this.mCallBlockReason = callBlockReason;
        this.mCallScreeningAppName = callScreeningAppName;
        this.mCallScreeningComponentName = callScreeningComponentName;
        this.contactExists = contactExists;
    }

    /**
@@ -187,6 +209,7 @@ public class CallFilteringResult {
                .setCallBlockReason(callBlockReason)
                .setCallScreeningAppName(callScreeningAppName)
                .setCallScreeningComponentName(callScreeningComponentName)
                .setContactExists(contactExists || other.contactExists)
                .build();
    }

@@ -204,6 +227,7 @@ public class CallFilteringResult {
        if (shouldAddToCallLog != that.shouldAddToCallLog) return false;
        if (shouldShowNotification != that.shouldShowNotification) return false;
        if (mCallBlockReason != that.mCallBlockReason) return false;
        if (contactExists != that.contactExists) return false;

        if ((TextUtils.isEmpty(mCallScreeningAppName) &&
            TextUtils.isEmpty(that.mCallScreeningAppName)) &&
@@ -258,6 +282,10 @@ public class CallFilteringResult {
            sb.append(", notified");
        }

        if (contactExists) {
            sb.append(", contact exists");
        }

        if (mCallBlockReason != 0) {
            sb.append(", mCallBlockReason = ");
            sb.append(mCallBlockReason);
+2 −2
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ import java.util.concurrent.Executor;
public class IncomingCallFilterGraph {
    //TODO: Add logging for control flow.
    public static final String TAG = "IncomingCallFilterGraph";
    public static final CallFilteringResult DEFAULT_SCREENING_RESULT =
    public static final CallFilteringResult DEFAULT_RESULT =
            new CallFilteringResult.Builder()
                    .setShouldAllowCall(true)
                    .setShouldReject(false)
@@ -92,8 +92,8 @@ public class IncomingCallFilterGraph {
        mFinished = false;
        mContext = context;
        mTimeoutsAdapter = timeoutsAdapter;
        mCurrentResult = DEFAULT_SCREENING_RESULT;
        mLock = lock;
        mCurrentResult = DEFAULT_RESULT;
    }

    public void addFilter(CallFilter filter) {
+271 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.telecom.callfiltering;

import static com.android.server.telecom.callfiltering.IncomingCallFilterGraph.DEFAULT_RESULT;

import android.Manifest;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.CallLog;
import android.telecom.Log;
import android.telecom.TelecomManager;

import com.android.internal.telecom.ICallScreeningAdapter;
import com.android.internal.telecom.ICallScreeningService;
import com.android.server.telecom.Call;
import com.android.server.telecom.CallScreeningServiceHelper;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.ParcelableCallUtils;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

public class NewCallScreeningServiceFilter extends CallFilter {
    //TODO: Change the name of this class with CallScreeningServiceFilter
    public static final int PACKAGE_TYPE_CARRIER = 0;
    public static final int PACKAGE_TYPE_DEFAULT_DIALER = 1;
    public static final int PACKAGE_TYPE_USER_CHOSEN = 2;
    public static final long CALL_SCREENING_FILTER_TIMEOUT = 5000;

    private final Call mCall;
    private final String mPackageName;
    private final int mPackagetype;
    private PackageManager mPackageManager;
    private Context mContext;
    private CallScreeningServiceConnection mConnection;
    private final CallsManager mCallsManager;
    private CharSequence mAppName;
    private final ParcelableCallUtils.Converter mParcelableCallUtilsConverter;

    private class CallScreeningAdapter extends ICallScreeningAdapter.Stub {
        private CompletableFuture<CallFilteringResult> mResultFuture;

        public CallScreeningAdapter(CompletableFuture<CallFilteringResult> resultFuture) {
            mResultFuture = resultFuture;
        }

        @Override
        public void allowCall(String callId) {
            Long token = Binder.clearCallingIdentity();
            if (mCall == null || (!mCall.getId().equals(callId))) {
                Log.w(this, "allowCall, unknown call id: %s", callId);
            }
            mResultFuture.complete(DEFAULT_RESULT);
            Binder.restoreCallingIdentity(token);
            unbindCallScreeningService();
        }

        @Override
        public void disallowCall(String callId, boolean shouldReject,
                boolean shouldAddToCallLog, boolean shouldShowNotification,
                ComponentName componentName) {
            long token = Binder.clearCallingIdentity();
            if (mCall != null && mCall.getId().equals(callId)) {
                mResultFuture.complete(new CallFilteringResult.Builder()
                        .setShouldAllowCall(false)
                        .setShouldReject(shouldReject)
                        .setShouldSilence(false)
                        .setShouldAddToCallLog(shouldAddToCallLog
                                || packageTypeShouldAdd(mPackagetype))
                        .setShouldShowNotification(shouldShowNotification)
                        .setCallBlockReason(CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
                        .setCallScreeningAppName(mAppName)
                        .setCallScreeningComponentName(componentName.flattenToString())
                        .build());
            } else {
                Log.w(this, "disallowCall, unknown call id: %s", callId);
                mResultFuture.complete(DEFAULT_RESULT);
            }
            Binder.restoreCallingIdentity(token);
            unbindCallScreeningService();
        }

        @Override
        public void silenceCall(String callId) {
            long token = Binder.clearCallingIdentity();
            if (mCall != null && mCall.getId().equals(callId)) {
                mResultFuture.complete(new CallFilteringResult.Builder()
                        .setShouldAllowCall(true)
                        .setShouldReject(false)
                        .setShouldSilence(true)
                        .setShouldAddToCallLog(true)
                        .setShouldShowNotification(true)
                        .build());
            } else {
                Log.w(this, "silenceCall, unknown call id: %s" , callId);
                mResultFuture.complete(DEFAULT_RESULT);
            }
            Binder.restoreCallingIdentity(token);
            unbindCallScreeningService();
        }

        @Override
        public void screenCallFurther(String callId) {
            long token = Binder.clearCallingIdentity();
            if (mPackagetype != PACKAGE_TYPE_DEFAULT_DIALER) {
                throw new SecurityException("Only the default/system dialer may request screen via"
                    + "background call audio");
            }
            // TODO: add permission check for the additional role-based permission

            if (mCall != null && mCall.getId().equals(callId)) {
                mResultFuture.complete(new CallFilteringResult.Builder()
                        .setShouldAllowCall(true)
                        .setShouldReject(false)
                        .setShouldSilence(false)
                        .setShouldScreenViaAudio(true)
                        .build());
            } else {
                Log.w(this, "screenCallFurther, unknown call id: %s", callId);
                mResultFuture.complete(DEFAULT_RESULT);
            }
            Binder.restoreCallingIdentity(token);
            unbindCallScreeningService();
        }
    }

    private class CallScreeningServiceConnection implements ServiceConnection {
        private CompletableFuture<CallFilteringResult> mResultFuture;

        public CallScreeningServiceConnection(CompletableFuture<CallFilteringResult> resultFuture) {
            mResultFuture = resultFuture;
        }

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            ICallScreeningService callScreeningService =
                    ICallScreeningService.Stub.asInterface(service);

            try {
                callScreeningService.screenCall(new CallScreeningAdapter(mResultFuture),
                        mParcelableCallUtilsConverter.
                                toParcelableCallForScreening(mCall, isSystemDialer()));
            } catch (RemoteException e) {
                Log.e(this, e, "Failed to set the call screening adapter");
                mResultFuture.complete(DEFAULT_RESULT);
            }
            Log.i(this, "Binding completed.");
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mResultFuture.complete(DEFAULT_RESULT);
            Log.i(this, "Service disconnected.");
        }

        @Override
        public void onBindingDied(ComponentName name) {
            mResultFuture.complete(DEFAULT_RESULT);
            Log.i(this, "Binding died.");
        }

        @Override
        public void onNullBinding(ComponentName name) {
            mResultFuture.complete(DEFAULT_RESULT);
            Log.i(this, "Null binding.");
        }
    }

    public NewCallScreeningServiceFilter(
            Call call,
            String packageName,
            int packageType,
            Context context,
            CallsManager callsManager,
            CallScreeningServiceHelper.AppLabelProxy appLabelProxy,
            ParcelableCallUtils.Converter parcelableCallUtilsConverter) {
        super();
        mCall = call;
        mPackageName = packageName;
        mPackagetype = packageType;
        mContext = context;
        mPackageManager = mContext.getPackageManager();
        mCallsManager = callsManager;
        mAppName = appLabelProxy.getAppLabel(mPackageName);
        mParcelableCallUtilsConverter = parcelableCallUtilsConverter;
    }

    @Override
    public CompletionStage<CallFilteringResult> startFilterLookup(CallFilteringResult priorStageResult) {
        if (mPackageName == null) {
            return CompletableFuture.completedFuture(DEFAULT_RESULT);
        }

        if (!priorStageResult.shouldAllowCall) {
            // Call already blocked by other filters, no need to bind to call screening service.
            return CompletableFuture.completedFuture(DEFAULT_RESULT);
        }

        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);
        }

        CompletableFuture<CallFilteringResult> resultFuture = new CompletableFuture<>();

        bindCallScreeningService(resultFuture);
        return resultFuture;
    }

    private boolean hasReadContactsPermission() {
        int permission = PackageManager.PERMISSION_DENIED;
        if (mPackagetype == PACKAGE_TYPE_CARRIER || mPackagetype == PACKAGE_TYPE_DEFAULT_DIALER) {
            permission = PackageManager.PERMISSION_GRANTED;
        } else if (mPackageManager != null) {
            permission = mPackageManager.checkPermission(Manifest.permission.READ_CONTACTS,
                    mPackageName);
        }
        return permission == PackageManager.PERMISSION_GRANTED;
    }

    private void bindCallScreeningService(
            CompletableFuture<CallFilteringResult> resultFuture) {
        mConnection = new CallScreeningServiceConnection(resultFuture);
        if (!CallScreeningServiceHelper.bindCallScreeningService(mContext,
                mCallsManager.getCurrentUserHandle(), mPackageName, mConnection)) {
            Log.i(this, "Call screening service binding failed.");
            resultFuture.complete(DEFAULT_RESULT);
        }
    }

    private void unbindCallScreeningService() {
        if (mConnection != null) {
            mContext.unbindService(mConnection);
        }
        mConnection = null;
    }

    private boolean isSystemDialer() {
        if (mPackagetype != PACKAGE_TYPE_DEFAULT_DIALER) {
            return false;
        } else {
            return mPackageName.equals(TelecomManager.from(mContext).getSystemDialerPackage());
        }
    }

    private boolean packageTypeShouldAdd(int packageType) {
        return packageType == PACKAGE_TYPE_CARRIER;
    }

}
+320 −0

File added.

Preview size limit exceeded, changes collapsed.