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

Commit 1b1e39ad authored by Hall Liu's avatar Hall Liu Committed by Android (Google) Code Review
Browse files

Merge changes from topics "call-screening-refactor", "composer-call-screening" into sc-dev

* changes:
  Add APIs to support call composer filtering
  Refactor CallScreeningService's internal structure
parents 57cc7120 d4c62b99
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -39207,16 +39207,22 @@ package android.telecom {
  }
  public static class CallScreeningService.CallResponse {
    method public int getCallComposerAttachmentsToShow();
    method public boolean getDisallowCall();
    method public boolean getRejectCall();
    method public boolean getSilenceCall();
    method public boolean getSkipCallLog();
    method public boolean getSkipNotification();
    field public static final int CALL_COMPOSER_ATTACHMENT_LOCATION = 2; // 0x2
    field public static final int CALL_COMPOSER_ATTACHMENT_PICTURE = 1; // 0x1
    field public static final int CALL_COMPOSER_ATTACHMENT_PRIORITY = 8; // 0x8
    field public static final int CALL_COMPOSER_ATTACHMENT_SUBJECT = 4; // 0x4
  }
  public static class CallScreeningService.CallResponse.Builder {
    ctor public CallScreeningService.CallResponse.Builder();
    method public android.telecom.CallScreeningService.CallResponse build();
    method @NonNull public android.telecom.CallScreeningService.CallResponse.Builder setCallComposerAttachmentsToShow(int);
    method public android.telecom.CallScreeningService.CallResponse.Builder setDisallowCall(boolean);
    method public android.telecom.CallScreeningService.CallResponse.Builder setRejectCall(boolean);
    method @NonNull public android.telecom.CallScreeningService.CallResponse.Builder setSilenceCall(boolean);
+2 −2
Original line number Diff line number Diff line
@@ -10769,7 +10769,7 @@ package android.telecom {
    method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
    method @Nullable public final String getTelecomCallId();
    method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
    method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
    method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean);
    method public final void resetConnectionTime();
    method public void setCallDirection(int);
    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
@@ -10946,7 +10946,7 @@ package android.telecom {
  }
  public final class RemoteConnection {
    method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
    method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean);
    method @Deprecated public void setAudioState(android.telecom.AudioState);
  }
+22 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 android.telecom;

/**
 * {@hide}
 */
parcelable CallScreeningService.ParcelableCallResponse;
+254 −19
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.telecom;

import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -30,12 +31,18 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;

import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.ICallScreeningAdapter;
import com.android.internal.telecom.ICallScreeningService;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;

/**
 * This service can be implemented by the default dialer (see
 * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
@@ -132,7 +139,10 @@ public abstract class CallScreeningService extends Service {
                                .createFromParcelableCall((ParcelableCall) args.arg2);
                        onScreenCall(callDetails);
                        if (callDetails.getCallDirection() == Call.Details.DIRECTION_OUTGOING) {
                            mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
                            mCallScreeningAdapter.onScreeningResponse(
                                    callDetails.getTelecomCallId(),
                                    new ComponentName(getPackageName(), getClass().getName()),
                                    null);
                        }
                    } catch (RemoteException e) {
                        Log.w(this, "Exception when screening call: " + e);
@@ -157,16 +167,171 @@ public abstract class CallScreeningService extends Service {

    private ICallScreeningAdapter mCallScreeningAdapter;

    /*
     * Information about how to respond to an incoming call.
    /**
     * Parcelable version of {@link CallResponse} used to do IPC.
     * @hide
     */
    public static class ParcelableCallResponse implements Parcelable {
        private final boolean mShouldDisallowCall;
        private final boolean mShouldRejectCall;
        private final boolean mShouldSilenceCall;
        private final boolean mShouldSkipCallLog;
        private final boolean mShouldSkipNotification;
        private final boolean mShouldScreenCallViaAudioProcessing;

        private final int mCallComposerAttachmentsToShow;

        private ParcelableCallResponse(
                boolean shouldDisallowCall,
                boolean shouldRejectCall,
                boolean shouldSilenceCall,
                boolean shouldSkipCallLog,
                boolean shouldSkipNotification,
                boolean shouldScreenCallViaAudioProcessing,
                int callComposerAttachmentsToShow) {
            mShouldDisallowCall = shouldDisallowCall;
            mShouldRejectCall = shouldRejectCall;
            mShouldSilenceCall = shouldSilenceCall;
            mShouldSkipCallLog = shouldSkipCallLog;
            mShouldSkipNotification = shouldSkipNotification;
            mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
            mCallComposerAttachmentsToShow = callComposerAttachmentsToShow;
        }

        protected ParcelableCallResponse(Parcel in) {
            mShouldDisallowCall = in.readBoolean();
            mShouldRejectCall = in.readBoolean();
            mShouldSilenceCall = in.readBoolean();
            mShouldSkipCallLog = in.readBoolean();
            mShouldSkipNotification = in.readBoolean();
            mShouldScreenCallViaAudioProcessing = in.readBoolean();
            mCallComposerAttachmentsToShow = in.readInt();
        }

        public CallResponse toCallResponse() {
            return new CallResponse.Builder()
                    .setDisallowCall(mShouldDisallowCall)
                    .setRejectCall(mShouldRejectCall)
                    .setSilenceCall(mShouldSilenceCall)
                    .setSkipCallLog(mShouldSkipCallLog)
                    .setSkipNotification(mShouldSkipNotification)
                    .setShouldScreenCallViaAudioProcessing(mShouldScreenCallViaAudioProcessing)
                    .setCallComposerAttachmentsToShow(mCallComposerAttachmentsToShow)
                    .build();
        }

        public boolean shouldDisallowCall() {
            return mShouldDisallowCall;
        }

        public boolean shouldRejectCall() {
            return mShouldRejectCall;
        }

        public boolean shouldSilenceCall() {
            return mShouldSilenceCall;
        }

        public boolean shouldSkipCallLog() {
            return mShouldSkipCallLog;
        }

        public boolean shouldSkipNotification() {
            return mShouldSkipNotification;
        }

        public boolean shouldScreenCallViaAudioProcessing() {
            return mShouldScreenCallViaAudioProcessing;
        }

        public int getCallComposerAttachmentsToShow() {
            return mCallComposerAttachmentsToShow;
        }

        public static final Creator<ParcelableCallResponse> CREATOR =
                new Creator<ParcelableCallResponse>() {
                    @Override
                    public ParcelableCallResponse createFromParcel(Parcel in) {
                        return new ParcelableCallResponse(in);
                    }

                    @Override
                    public ParcelableCallResponse[] newArray(int size) {
                        return new ParcelableCallResponse[size];
                    }
                };

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeBoolean(mShouldDisallowCall);
            dest.writeBoolean(mShouldRejectCall);
            dest.writeBoolean(mShouldSilenceCall);
            dest.writeBoolean(mShouldSkipCallLog);
            dest.writeBoolean(mShouldSkipNotification);
            dest.writeBoolean(mShouldScreenCallViaAudioProcessing);
            dest.writeInt(mCallComposerAttachmentsToShow);
        }
    }

    /**
     * Information about how to respond to an incoming call. Call screening apps can construct an
     * instance of this class using {@link CallResponse.Builder}.
     */
    public static class CallResponse {
        /**
         * Bit flag indicating whether to show the picture attachment for call composer.
         *
         * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
         */
        public static final int CALL_COMPOSER_ATTACHMENT_PICTURE = 1;

        /**
         * Bit flag indicating whether to show the location attachment for call composer.
         *
         * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
         */
        public static final int CALL_COMPOSER_ATTACHMENT_LOCATION = 1 << 1;

        /**
         * Bit flag indicating whether to show the subject attachment for call composer.
         *
         * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
         */
        public static final int CALL_COMPOSER_ATTACHMENT_SUBJECT = 1 << 2;

        /**
         * Bit flag indicating whether to show the priority attachment for call composer.
         *
         * Used with {@link Builder#setCallComposerAttachmentsToShow(int)}.
         */
        public static final int CALL_COMPOSER_ATTACHMENT_PRIORITY = 1 << 3;

        /** @hide */
        @Retention(RetentionPolicy.SOURCE)
        @IntDef(prefix = "CALL_COMPOSER_ATTACHMENT_", flag = true,
                value = {
                        CALL_COMPOSER_ATTACHMENT_PICTURE,
                        CALL_COMPOSER_ATTACHMENT_LOCATION,
                        CALL_COMPOSER_ATTACHMENT_SUBJECT,
                        CALL_COMPOSER_ATTACHMENT_PRIORITY
                }
        )
        public @interface CallComposerAttachmentType {}

        private static final int NUM_CALL_COMPOSER_ATTACHMENT_TYPES = 4;

        private final boolean mShouldDisallowCall;
        private final boolean mShouldRejectCall;
        private final boolean mShouldSilenceCall;
        private final boolean mShouldSkipCallLog;
        private final boolean mShouldSkipNotification;
        private final boolean mShouldScreenCallViaAudioProcessing;
        private final int mCallComposerAttachmentsToShow;

        private CallResponse(
                boolean shouldDisallowCall,
@@ -174,7 +339,8 @@ public abstract class CallScreeningService extends Service {
                boolean shouldSilenceCall,
                boolean shouldSkipCallLog,
                boolean shouldSkipNotification,
                boolean shouldScreenCallViaAudioProcessing) {
                boolean shouldScreenCallViaAudioProcessing,
                int callComposerAttachmentsToShow) {
            if (!shouldDisallowCall
                    && (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
                throw new IllegalStateException("Invalid response state for allowed call.");
@@ -190,6 +356,7 @@ public abstract class CallScreeningService extends Service {
            mShouldSkipNotification = shouldSkipNotification;
            mShouldSilenceCall = shouldSilenceCall;
            mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
            mCallComposerAttachmentsToShow = callComposerAttachmentsToShow;
        }

        /*
@@ -237,6 +404,49 @@ public abstract class CallScreeningService extends Service {
            return mShouldScreenCallViaAudioProcessing;
        }

        /**
         * @return A bitmask of call composer attachments that should be shown to the user.
         */
        public @CallComposerAttachmentType int getCallComposerAttachmentsToShow() {
            return mCallComposerAttachmentsToShow;
        }

        /** @hide */
        public ParcelableCallResponse toParcelable() {
            return new ParcelableCallResponse(
                    mShouldDisallowCall,
                    mShouldRejectCall,
                    mShouldSilenceCall,
                    mShouldSkipCallLog,
                    mShouldSkipNotification,
                    mShouldScreenCallViaAudioProcessing,
                    mCallComposerAttachmentsToShow
            );
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            CallResponse that = (CallResponse) o;
            return mShouldDisallowCall == that.mShouldDisallowCall &&
                    mShouldRejectCall == that.mShouldRejectCall &&
                    mShouldSilenceCall == that.mShouldSilenceCall &&
                    mShouldSkipCallLog == that.mShouldSkipCallLog &&
                    mShouldSkipNotification == that.mShouldSkipNotification &&
                    mShouldScreenCallViaAudioProcessing
                            == that.mShouldScreenCallViaAudioProcessing &&
                    mCallComposerAttachmentsToShow == that.mCallComposerAttachmentsToShow;
        }

        @Override
        public int hashCode() {
            return Objects.hash(mShouldDisallowCall, mShouldRejectCall, mShouldSilenceCall,
                    mShouldSkipCallLog, mShouldSkipNotification,
                    mShouldScreenCallViaAudioProcessing,
                    mCallComposerAttachmentsToShow);
        }

        public static class Builder {
            private boolean mShouldDisallowCall;
            private boolean mShouldRejectCall;
@@ -244,6 +454,7 @@ public abstract class CallScreeningService extends Service {
            private boolean mShouldSkipCallLog;
            private boolean mShouldSkipNotification;
            private boolean mShouldScreenCallViaAudioProcessing;
            private int mCallComposerAttachmentsToShow = -1;

            /**
             * Sets whether the incoming call should be blocked.
@@ -329,6 +540,38 @@ public abstract class CallScreeningService extends Service {
                return this;
            }

            /**
             * Sets the call composer attachments that should be shown to the user.
             *
             * Attachments that are not shown will not be passed to the in-call UI responsible for
             * displaying the call to the user.
             *
             * If this method is not called on a {@link Builder}, all attachments will be shown,
             * except pictures, which will only be shown to users if the call is from a contact.
             *
             * Setting attachments to show will have no effect if the call screening service does
             * not belong to the same package as the system dialer (as returned by
             * {@link TelecomManager#getSystemDialerPackage()}).
             *
             * @param callComposerAttachmentsToShow A bitmask of call composer attachments to show.
             */
            public @NonNull Builder setCallComposerAttachmentsToShow(
                    @CallComposerAttachmentType int callComposerAttachmentsToShow) {
                // If the argument is less than zero (meaning unset), no-op since the conversion
                // to/from the parcelable version may call with that value.
                if (callComposerAttachmentsToShow < 0) {
                    return this;
                }

                if ((callComposerAttachmentsToShow
                        & (1 << NUM_CALL_COMPOSER_ATTACHMENT_TYPES)) != 0) {
                    throw new IllegalArgumentException("Attachment types must match the ones"
                            + " defined in CallResponse");
                }
                mCallComposerAttachmentsToShow = callComposerAttachmentsToShow;
                return this;
            }

            public CallResponse build() {
                return new CallResponse(
                        mShouldDisallowCall,
@@ -336,7 +579,8 @@ public abstract class CallScreeningService extends Service {
                        mShouldSilenceCall,
                        mShouldSkipCallLog,
                        mShouldSkipNotification,
                        mShouldScreenCallViaAudioProcessing);
                        mShouldScreenCallViaAudioProcessing,
                        mCallComposerAttachmentsToShow);
            }
       }
    }
@@ -423,21 +667,12 @@ public abstract class CallScreeningService extends Service {
    public final void respondToCall(@NonNull Call.Details callDetails,
            @NonNull CallResponse response) {
        try {
            if (response.getDisallowCall()) {
                mCallScreeningAdapter.disallowCall(
            mCallScreeningAdapter.onScreeningResponse(
                    callDetails.getTelecomCallId(),
                        response.getRejectCall(),
                        !response.getSkipCallLog(),
                        !response.getSkipNotification(),
                        new ComponentName(getPackageName(), getClass().getName()));
            } else if (response.getSilenceCall()) {
                mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
            } else if (response.getShouldScreenCallViaAudioProcessing()) {
                mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId());
            } else {
                mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
            }
                    new ComponentName(getPackageName(), getClass().getName()),
                    response.toParcelable());
        } catch (RemoteException e) {
            Log.e(this, e, "Got remote exception when returning response");
        }
    }
}
+10 −1
Original line number Diff line number Diff line
@@ -3390,11 +3390,20 @@ public abstract class Connection extends Conferenceable {
     *                  {@code true}, {@link #onDisconnect()} will be called soon after
     *                  this is called.
     * @param isInContacts Indicates whether the caller is in the user's contacts list.
     * @param callScreeningResponse The response that was returned from the
     *                              {@link CallScreeningService} that handled this call. If no
     *                              response was received from a call screening service,
     *                              this will be {@code null}.
     * @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
     *                                  system dialer. If {@code callScreeningResponse} is
     *                                  {@code null}, this will be {@code false}.
     * @hide
     */
    @SystemApi
    @RequiresPermission(Manifest.permission.READ_CONTACTS)
    public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) { }
    public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
            @Nullable CallScreeningService.CallResponse callScreeningResponse,
            boolean isResponseFromSystemDialer) { }

    static String toLogSafePhoneNumber(String number) {
        // For unknown number, log empty string.
Loading